流程控制
流程控制就是控制代碼的執(zhí)行順序。
正常代碼都是按順序一行一行執(zhí)行的狗唉,這叫順序流程初烘。
如果想控制程序在滿足某個(gè)條件時(shí)才執(zhí)行特定的代碼塊,就需要條件控制分俯。
如果想控制程序在滿足某個(gè)條件時(shí)重復(fù)執(zhí)行特定的代碼塊肾筐,就需要循環(huán)控制。
順序
上面講完了澳迫。
條件
使用關(guān)鍵字 if...elif...else
來(lái)構(gòu)造條件流程局齿,其中elif和else是可選的,每一個(gè)條件判斷語(yǔ)句后面以冒號(hào)':'結(jié)尾橄登,條件成立時(shí)執(zhí)行代的碼塊跟在下一行并縮進(jìn)抓歼。注意代碼塊不能全是空行或注釋,如果想什么也不做拢锹,可以寫個(gè)pass
谣妻。
python 沒(méi)有像其他語(yǔ)言的 switch...case
關(guān)鍵字。
if x == 0: # 條件不需要括號(hào)包起來(lái)卒稳,行尾要有個(gè)冒號(hào)
print('x等于0') # 需要縮進(jìn)蹋半,一般建議4個(gè)空格,且行尾不需要分號(hào)
elif x > 0: # elif 代碼塊可以多個(gè)
print('x大于0')
else: # 當(dāng)前面所有條件都不滿足時(shí)充坑,執(zhí)行下面的代碼塊
print('x小于0')
if x is None: # 判斷 x 等于 None减江, is用于判斷兩個(gè)對(duì)象的id值是否一樣,id一樣表示是同一個(gè)對(duì)象
pass # pass表示什么也不做
if x is not None: # 判斷 x 不等于 None捻爷, not表示取反
print('x = ', x)
條件語(yǔ)句可以使用括號(hào)辈灼,從而改變條件的判斷順序。
if x == 0 or (y == 0 and z == 0):
print('x等于0')
如果條件語(yǔ)句太長(zhǎng)也榄,不好放在一行巡莹,可以用括號(hào)包起來(lái),括號(hào)里的語(yǔ)句可以換行
if (x == 0 or y == 0
or z == 0): # 括號(hào)包起來(lái)后可以換行
print('x等于0')
或者
if (x == 0
or y == 0
or z == 0
): # 括號(hào)包起來(lái)后可以換行
print('x等于0')
循環(huán)
使用 while
或 for...in
來(lái)構(gòu)造循環(huán)流程。
- while
只要 while 后面的條件語(yǔ)句成立降宅,就執(zhí)行后面跟著的縮進(jìn)代碼塊, 下面例子的輸出 0 ~ 9 整數(shù)骂远。
x = 0
whlile x < 10: # 當(dāng)變量 x < 10 時(shí),執(zhí)行下面縮進(jìn)的兩行代碼腰根,并再次回到本語(yǔ)句激才,直到 x >= 10
print(x)
x += 1
所有循環(huán)條件語(yǔ)句同樣可以使用括號(hào)和行號(hào)。
- for...in
for...in
相對(duì)特殊一點(diǎn)唠雕,先看例子, 下面例子的輸出 0 ~ 9 整數(shù)贸营。
for x in range(10): # 對(duì)每個(gè)變量 x 值(0 <= x < 10,步長(zhǎng)為1岩睁, 即等于range(10)的元素值), 執(zhí)行下面縮進(jìn)的代碼
print(x)
for x in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: # 對(duì)每個(gè)變量 x 值 (x 等于列表的元素值), 執(zhí)行下面縮進(jìn)的代碼
print(x)
for...in
循環(huán)在 python 中的使用頻率很高钞脂,而且其背后有一些復(fù)雜的約定和機(jī)制仇箱,完全講清楚這些需要后面'面向?qū)ο?章節(jié)的相關(guān)知識(shí)匣椰,這里先大致說(shuō)一下。
在 for...in
循環(huán)中蛀缝,不是什么東西都可以放在關(guān)鍵字 in
后面刘莹,必須是一個(gè)可迭代對(duì)象(迭代就是一個(gè)一個(gè)遍歷獲取的意思)阎毅,可迭代對(duì)象有兩個(gè)特點(diǎn):
1 對(duì)象可以作為參數(shù)傳給內(nèi)置函數(shù) iter() 并返回一個(gè)迭代器對(duì)象。
2 返回的迭代器對(duì)象有__next__()方法, 調(diào)用這個(gè)方法一次可以返回一個(gè)值点弯,調(diào)用一次返回一個(gè)扇调,直到?jīng)]有可返回的了,則拋出一個(gè)異常(什么是異常后面會(huì)講)抢肛。
實(shí)際上for...in
循環(huán)就是執(zhí)行上面1狼钮、2個(gè)步驟來(lái)實(shí)現(xiàn)循環(huán)的,以 for x in range(10)
為例捡絮,背后可以分解為下面幾個(gè)步驟:
1 iter(range(10)) 返回一個(gè)迭代器對(duì)象熬芜,假設(shè)為a.
2 循環(huán)調(diào)用 a.__next__(), 并將返回值賦給 x,直到出現(xiàn)異常則退出循環(huán)福稳。
- continue 和 break
continue
用來(lái)讓循環(huán)中的某一次處理提前結(jié)束涎拉,并進(jìn)行下一輪。
for x in range(10):
if x == 5: # x == 5時(shí)的圆,直接返回for語(yǔ)句鼓拧,下面的print(x)將不會(huì)執(zhí)行,也就是只打印 0 ~ 4越妈,6 ~ 9
continue
print(x)
break
用來(lái)結(jié)束整個(gè)循環(huán)毁枯。
for x in range(10):
if x == 5: # x == 5時(shí),將退出整個(gè)循環(huán)叮称,也就是只打印 0 ~ 4
break
print(x)
函數(shù)
把一部分具有相關(guān)性的代碼封裝起來(lái),用一個(gè)名字來(lái)代替它,這就是函數(shù)瓤檐,當(dāng)需要使用這部分代碼時(shí)赂韵,不需要把這些代碼再寫一遍,只要按一定書寫規(guī)則調(diào)用函數(shù)就可以了挠蛉。
函數(shù)能提高代碼重復(fù)利用率祭示,并能提高代碼可讀性,可以說(shuō)稍微復(fù)雜一點(diǎn)的代碼都離不開(kāi)函數(shù)谴古。
定義函數(shù)
使用關(guān)鍵字def來(lái)定義函數(shù)质涛,函數(shù)可以有參數(shù) (寫在括號(hào)里,多個(gè)參數(shù)用逗號(hào)隔開(kāi)) 和返回值 (使用關(guān)鍵字 return) 掰担,沒(méi)有明確寫返回值的則默認(rèn)返回 None汇陆。
def sum(a, b): # 定義求和函數(shù) sum, a、b 為輸入?yún)?shù)
s = a + b
return s # s 為返回值
def hello(): # 定義函數(shù) hello, 沒(méi)有輸入?yún)?shù)
print('hello')
# 沒(méi)有顯式的返回值带饱,則默認(rèn)返回None
調(diào)用函數(shù)
調(diào)用函數(shù)和數(shù)學(xué)里的用法類似毡代,寫上函數(shù)名和參數(shù)就可以, 比如調(diào)用前面例子里的函數(shù)。
v = sum(1, 2) # 調(diào)用函數(shù) sum 計(jì)算 1 和 2 的和勺疼,并把返回值 3 賦值給 v
print(sum(1, 2)) # 打印 sum(1, 2) 的返回值 3
hello() # 調(diào)用函數(shù) hello教寂,將打印 'hello'
函數(shù)內(nèi)部可以再調(diào)用函數(shù),甚至可以調(diào)用自己执庐,調(diào)用自己的函數(shù)叫作遞歸函數(shù)酪耕。
程序在進(jìn)入函數(shù)內(nèi)部時(shí),會(huì)分配一定的內(nèi)存(叫棧幀)轨淌,在函數(shù)退出時(shí)迂烁,釋放這些內(nèi)存,所以如果函數(shù)調(diào)用層次太深(特別是遞歸調(diào)用)的話猿诸,可能會(huì)消耗太多內(nèi)存婚被,導(dǎo)致程序掛掉。
參數(shù)傳遞
把一個(gè)變量作為參數(shù)傳遞給函數(shù)梳虽,在函數(shù)內(nèi)部看到的變量實(shí)際不是函數(shù)外部的那個(gè)變量址芯,而是一個(gè)重新創(chuàng)建的變量,只是變量所引用的對(duì)象和函數(shù)外部變量所引用的對(duì)象一樣窜觉。因此在函數(shù)內(nèi)部對(duì)參數(shù)變量的修改谷炸,并不會(huì)改變函數(shù)外部變量的值,但是禀挫,如果函數(shù)內(nèi)部改變了參數(shù)所引用對(duì)象的值旬陡,則函數(shù)外部變量所引用對(duì)象的值也會(huì)跟著改變。
>>> def clear(a):
... a = 0 # 這里的 a 和函數(shù)外部的 a 不是同一個(gè)變量
...
>>> a = 10
>>> clear(a)
>>> a # a 的值并沒(méi)有被改變
10
>>>
>>> def set(a):
... a[0] = 0 # 改變參數(shù)變量引用對(duì)象的值
...
>>> a = [10, 10, 10]
>>> set(a)
>>> a # a 引用對(duì)象的值被改變了
[0, 10, 10]
>>>
python 的函數(shù)支持非常靈活的參數(shù)定義和傳遞方式语婴。
-
位置參數(shù)
位置參數(shù)(Required arguments), 指調(diào)用函數(shù)時(shí)描孟,參數(shù)的個(gè)數(shù)和順序必須和定義時(shí)嚴(yán)格一樣驶睦。def foo(a, b, c): print(a, b, c) foo(1, 2, 3) # 1, 2, 3將分別被傳給函數(shù)參數(shù)的a, b, c foo(1,2) # 少一個(gè)參數(shù),運(yùn)行將報(bào)錯(cuò)
-
可變參數(shù)
可變參數(shù)(Variable-length arguments), 指函數(shù)的參數(shù)個(gè)數(shù)是可變的匿醒,每次調(diào)用傳入的參數(shù)個(gè)數(shù)可以不一樣场航。
如果同時(shí)有位置參數(shù)和可變參數(shù),可變參數(shù)要在后面廉羔。def foo(a, b, *args): # args前面有個(gè)'*'表示是可變參數(shù), 可變參數(shù)一般建議取名為args print(a, b) for v in args: # 可變參數(shù)實(shí)際上會(huì)被組裝成列表溉痢,再傳遞給函數(shù) print(v) foo(1, 2) # 可變參數(shù)個(gè)數(shù)為0 foo(1, 2, 3, 4, 5) # 可變參數(shù)個(gè)數(shù)為3, 分別是3, 4, 5 a = [3, 4, 5] foo(1, 2, *a) # 可以把列表傳給可變參數(shù),前面要加個(gè)*, 這里效果和foo(1, 2, 3, 4, 5)一樣
-
關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)(Keyword arguments), 指?jìng)鬟f函數(shù)參數(shù)時(shí)憋他,必須通過(guò)賦值形式顯示寫上參數(shù)名孩饼, 關(guān)鍵字參數(shù)傳遞順序可以和定義順序不同。
關(guān)鍵字參數(shù)的定義跟在可變參數(shù)后面竹挡,如果沒(méi)有可變參數(shù)镀娶,則在關(guān)鍵字參數(shù)前面定義一個(gè) * 。def foo(a, b, *args此迅,c, d): # 可變參數(shù)后面的參數(shù) c 和 d 為關(guān)鍵字參數(shù) print(a, b) # 位置參數(shù) for v in args: # 可變參數(shù) print(v) print(c, d) # 關(guān)鍵字參數(shù) foo(1, 2, 3, 4, 5, d=7, c=6) # a為1汽畴,b為2,args為(3, 4, 5), c為6耸序,d為7 def foo(a, b, *忍些,c, d): # '*' 后面的參數(shù) c 和 d 為關(guān)鍵字參數(shù) print(a, b, c, d) foo(1, 2, c=6, d=7) # a為1,b為2, c為6坎怪,d為7
另外罢坝,位置參數(shù)實(shí)際上也可以用關(guān)鍵字參數(shù)的方式傳遞。
def foo(a, b, c): # 位置參數(shù) print(a, b, c) foo(b=2, a=1, c=3) # 以關(guān)鍵字方式傳遞參數(shù)搅窿,1, 2, 3將分別被傳給函數(shù)參數(shù)的a, b, c foo(a=1, b=2) # 少一個(gè)參數(shù)嘁酿,運(yùn)行將報(bào)錯(cuò)
以上方式要求傳遞參數(shù)時(shí),參數(shù)個(gè)數(shù)必須和定義時(shí)個(gè)數(shù)一樣男应,如果想定義個(gè)數(shù)可變的關(guān)鍵字參數(shù)闹司,可以在關(guān)鍵字參數(shù)前加兩個(gè) * 參數(shù)。
def foo(a, b, **kwargs): # kwargs前面有兩個(gè)'*'沐飘,表示是可變關(guān)鍵字參數(shù), 可變關(guān)鍵字參數(shù)一般建議取名為kwargs print(a, b) for k, v in kwargs.items(): # 可變關(guān)鍵字參數(shù)實(shí)際上會(huì)被組裝成字典游桩,再傳遞給函數(shù) print(v) foo(1, 2) # 可變關(guān)鍵字參數(shù)個(gè)數(shù)為0 foo(1, 2, e=3, f=4, g=5) # 可變關(guān)鍵字參數(shù)個(gè)數(shù)為3, 分別是'e':3, 'f':4, 'g':5 a = {'e':3, 'f':4, 'g':5} foo(1, 2, **a) # 可以把字典給可變關(guān)鍵字參數(shù),前面要加兩個(gè)*, 這里效果和foo(1, 2, e=3, f=4, g=5)一樣
-
默認(rèn)參數(shù)
位置參數(shù)和關(guān)鍵字參數(shù)都可以有默認(rèn)值耐朴,調(diào)用函數(shù)時(shí)借卧,如果沒(méi)有顯示參入?yún)?shù)值,則該參數(shù)值為指定的默認(rèn)值筛峭。
有默認(rèn)值的位置參數(shù)必須在沒(méi)有默認(rèn)值的位置參數(shù)后面铐刘,有默認(rèn)值的關(guān)鍵字參數(shù)則沒(méi)有此限制。def foo(a, b, c=3): # 參數(shù)c為默認(rèn)參數(shù)影晓,默認(rèn)值為3 print(a, b, c) foo(1, 2) # 相當(dāng)于 foo(1, 2, 3) def foo(a, b, *, c=3, d): # 參數(shù)c為默認(rèn)參數(shù)镰吵,默認(rèn)值為3 print(a, b, c, d) foo(1, 2, d=4) # 相當(dāng)于 foo(1, 2, c=3, d=4) def foo(a, b=2, *, c=3, d): # 參數(shù)b為默認(rèn)參數(shù)檩禾,默認(rèn)值為2, 參數(shù)c為默認(rèn)參數(shù),默認(rèn)值為3 print(a, b, c, d) foo(1, d=4) # 相當(dāng)于 foo(1, 2, c=3, d=4)
-
任意參數(shù)函數(shù)
下面形式的函數(shù)捡遍,可以接受任意參數(shù), 這種函數(shù)參數(shù)定義方式在實(shí)現(xiàn)一些通用的高級(jí)函數(shù)時(shí)很有用锌订。def foo(*args, **kwargs): print(args, kwargs)
多返回值
函數(shù)可以返回任意數(shù)量的返回值,多個(gè)返回值使用逗號(hào)隔開(kāi)画株。
def test(a, b, c):
return a+1, b+2, c+3
a, b, c = test(1, 1, 1) # a為2,b為3啦辐,c為4
調(diào)用多返回值的函數(shù)時(shí)谓传,如果其中有些返回值是不關(guān)心的,可以使用下劃線'_'作為變量芹关。
a, _, _ = test(1, 1, 1) # a為2续挟,其他返回值不關(guān)心
多返回值實(shí)際上是以列表的形式返回的,因此也可以只用一個(gè)變量接收函數(shù)的多返回值侥衬。
a = test(1, 1, 1) # a為(2, 3, 4)
函數(shù)返回的多個(gè)返回值可以直接賦值給多個(gè)變量的原因是列表可以直接賦值給多個(gè)變量诗祸。
a = [1, 2, 3]
i, j, k = a # i為1, j為2, k為3
匿名函數(shù)
有些小函數(shù)特別是只被一個(gè)地方調(diào)用的函數(shù)轴总,有時(shí)候還要為它想一個(gè)名字就顯得很不方便直颅。python支持定義匿名函數(shù)(anonymous function),用來(lái)解決這類問(wèn)題怀樟。匿名函數(shù)用關(guān)鍵字 'lambda' 來(lái)定義功偿。
lambda [arg1 [,arg2,.....argn]]:expression # 不能有return,函數(shù)返回值為表達(dá)式值
前面說(shuō)過(guò)python里一切皆對(duì)象往堡,函數(shù)也是對(duì)象械荷, 因此可以直接賦值給變量或作為參數(shù)傳遞給其他函數(shù),匿名函數(shù)一般就是用在這種場(chǎng)景虑灰。
sum = lambda arg1, arg2: arg1 + arg2; # 定義求和匿名函數(shù)并賦值給變量sum吨瞎,arg1、arg2為參數(shù)
sum(1, 2) # 調(diào)用變量sum穆咐,就是調(diào)用求和匿名函數(shù)
def do(operation, a, b): # 參數(shù)operation是一個(gè)函數(shù)
return operation(a, b)
do(lambda arg1, arg2: arg1 * arg2, 1, 2) # 計(jì)算1颤诀、2的乘積,匿名函數(shù)作為參數(shù)傳遞
函數(shù)內(nèi)定義函數(shù)
python函數(shù)內(nèi)可以再定義函數(shù)庸娱,內(nèi)部的函數(shù)可以訪問(wèn)外部函數(shù)的變量着绊,內(nèi)部函數(shù)只能在外部函數(shù)內(nèi)調(diào)用,不能在外部函數(shù)外調(diào)用熟尉。
如果外部函數(shù)外想調(diào)用內(nèi)部函數(shù)归露,可以把內(nèi)部函數(shù)當(dāng)成返回值返回,外面通過(guò)返回值來(lái)調(diào)用內(nèi)部函數(shù)斤儿,這種用法其實(shí)是一種很特殊的機(jī)制剧包,有個(gè)專門名詞叫‘閉包’恐锦,遠(yuǎn)不是函數(shù)調(diào)用那么簡(jiǎn)單,后面會(huì)專門描述疆液。
def outer_func(a):
b = 2
def inner_func(): # 定義內(nèi)部函數(shù)
return a + b # 內(nèi)部函數(shù)調(diào)用外部函數(shù)的變量a和b
print(inner_func())
print(outer_func(1)) # 打印 3
def outer_func(a):
b = 2
def inner_func():
return a + b
return inner_func # 返回內(nèi)部函數(shù)
func = outer_func(1) # 通過(guò)返回值調(diào)用內(nèi)部函數(shù)
print(func()) # 打印 3一铅,很神奇,outer_func里的變量a和b的值好像被保存起來(lái)了