函數(shù)
Python的函數(shù)其實(shí)和Java的C的函數(shù)差不多欺殿,這里只講講和Java不同的地方。注意:函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用,棧就會(huì)加一層棧幀宠进,每當(dāng)函數(shù)返回,棧就會(huì)減一層棧幀藐翎。
函數(shù)別名
函數(shù)名其實(shí)就是指向一個(gè)函數(shù)對(duì)象的引用材蹬,完全可以把函數(shù)名賦給一個(gè)變量,相當(dāng)于給這個(gè)函數(shù)起了一個(gè)“別名”:
# 變量a指向abs函數(shù)
a = abs
print(a(-2.1))
定義函數(shù)
在Python中吝镣,定義一個(gè)函數(shù)要使用def語句堤器,依次寫出函數(shù)名、括號(hào)末贾、括號(hào)中的參數(shù)和冒號(hào):闸溃,然后,在縮進(jìn)塊中編寫函數(shù)體拱撵,函數(shù)的返回值用return語句返回辉川。
def add(a,b):
return a+b;
print(add(1,2))
如果沒有return語句,函數(shù)執(zhí)行完畢后也會(huì)返回結(jié)果拴测,只是結(jié)果為None乓旗。return None可以簡(jiǎn)寫為return。還有點(diǎn)和Java不一樣的是集索,函數(shù)必須定義在調(diào)用的前面屿愚,不然會(huì)報(bào)錯(cuò)汇跨。
- 空函數(shù)
如果想定義一個(gè)什么事也不做的空函數(shù),可以用pass語句:
# 定義了一個(gè)空函數(shù)
def add():
pass
pass語句什么都不做渺鹦,那有什么用扰法?實(shí)際上pass可以用來作為占位符,比如現(xiàn)在還沒想好怎么寫函數(shù)的代碼毅厚,就可以先放一個(gè)pass塞颁,讓代碼能運(yùn)行起來。
pass還可以用在其他語句里吸耿,比如:
x = 2
if x>0:
pass
缺少了pass祠锣,代碼運(yùn)行就會(huì)有語法錯(cuò)誤。
- 參數(shù)檢查
在Python里面咽安,當(dāng)我們調(diào)用函數(shù)的時(shí)候伴网,解釋器會(huì)幫我檢查函數(shù)參數(shù)的格式是否正確,不正確會(huì)報(bào)下面的錯(cuò)妆棒。
Traceback (most recent call last):
File "F:/Pythonproject/test_dir/__init__.py", line 90, in <module>
print(a(-2.1,0))
TypeError: abs() takes exactly one argument (2 given)
但是如果是自己調(diào)用的自己寫的函數(shù)或者是別人寫的話澡腾,那么假如我們傳入的參數(shù)類型不對(duì)的話,這個(gè)時(shí)候就很難讓調(diào)用者知道傳入的參數(shù)類型是錯(cuò)誤的糕珊,那么我們就需要為函數(shù)的參數(shù)做檢查了动分,對(duì)參數(shù)類型做檢查,只允許整數(shù)和浮點(diǎn)數(shù)類型的參數(shù)红选。數(shù)據(jù)類型檢查可以用內(nèi)置函數(shù)isinstance()實(shí)現(xiàn):
def add(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError('參數(shù)類型錯(cuò)誤')
else:
return a + b
add('A', 2)
添加了參數(shù)檢查后澜公,如果傳入錯(cuò)誤的參數(shù)類型,函數(shù)就可以拋出一個(gè)錯(cuò)誤:
Traceback (most recent call last):
File "F:/Pythonproject/test_dir/__init__.py", line 113, in <module>
add('A', 2)
File "F:/Pythonproject/test_dir/__init__.py", line 109, in add
raise TypeError('參數(shù)類型錯(cuò)誤')
TypeError: 參數(shù)類型錯(cuò)誤
- 返回多個(gè)值
在C或者java中喇肋,如果遇到需要返回不同結(jié)果的坟乾,一般都會(huì)用一個(gè)對(duì)象或者結(jié)構(gòu)體返回,但是在Python中函數(shù)可以返回多個(gè)值嗎蝶防?答案是肯定的
def get(a, b, c):
return a, b, c
a, b, c = get(1, 2, 3)
print(a, b, c)
Python是怎么辦到的寝姿?其實(shí)這個(gè)和我們上一篇講到的tuple有關(guān)簸淀,函數(shù)返回其實(shí)是一個(gè)tuple役电,本質(zhì)還是一個(gè)返回值幻妓,看到的知識(shí)一個(gè)假象而已壶栋。
函數(shù)的參數(shù)
Python的函數(shù)定義非常簡(jiǎn)單跑杭,但靈活度卻非常大盯串。除了正常定義的必選參數(shù)外抚垄,還可以使用默認(rèn)參數(shù)躏鱼、可變參數(shù)和關(guān)鍵字參數(shù)氮采,使得函數(shù)定義出來的接口,不但能處理復(fù)雜的參數(shù)染苛,還可以簡(jiǎn)化調(diào)用者的代碼鹊漠。
- 位置參數(shù)
def count(a, n):
count = 1
while n>0:
n = n-1
count = count * a
return count
print(count(2,3))
對(duì)于count(a, n)函數(shù)主到,參數(shù)a, n就是一個(gè)位置參數(shù)。
- 默認(rèn)參數(shù)
上面代碼count函數(shù)躯概,假如有許多地方普遍用到的都是n=2這個(gè)值登钥,我們不想給count函數(shù)傳入兩個(gè)參數(shù),這個(gè)時(shí)候我們就可以給n一個(gè)默認(rèn)的值娶靡,而n也就是默認(rèn)參數(shù)了:
def count(a, n=2,c=3):
count = 1
while n>0:
n = n-1
count = count * a
return count
# n與c都是默認(rèn)的牧牢,不用傳入
print(count(2))
# 按照順序的 指定n為3
print(count(2,3))
# 也可以指定調(diào)用的參數(shù)與確切的值
print(count(2,c = 6))
這么玩需要注意:一是必選參數(shù)在前,默認(rèn)參數(shù)在后姿锭,否則Python的解釋器會(huì)報(bào)錯(cuò)(思考一下為什么默認(rèn)參數(shù)不能放在必選參數(shù)前面)塔鳍;二是如何設(shè)置默認(rèn)參數(shù)。
當(dāng)函數(shù)有多個(gè)參數(shù)時(shí)呻此,把變化大的參數(shù)放前面轮纫,變化小的參數(shù)放后面。變化小的參數(shù)就可以作為默認(rèn)參數(shù)焚鲜。
下面有個(gè)有意思的代碼掌唾,帶你躺坑:
def test(t=[]):
t.append('test')
return t
print(test())
print(test())
['test']
['test', 'test']
這個(gè)代碼這么寫是有點(diǎn)問題的,不夠嚴(yán)謹(jǐn)忿磅,Python函數(shù)在定義的時(shí)候糯彬,默認(rèn)參數(shù)L的值就被計(jì)算出來了,即[]贝乎,因?yàn)槟J(rèn)參數(shù)t也是一個(gè)變量情连,它指向?qū)ο骩],每次調(diào)用該函數(shù)览效,如果改變了t的內(nèi)容却舀,則下次調(diào)用時(shí),默認(rèn)參數(shù)的內(nèi)容就變了锤灿,不再是函數(shù)定義時(shí)的[]了挽拔。
定義默認(rèn)參數(shù)要牢記一點(diǎn):默認(rèn)參數(shù)必須指向不變對(duì)象!
那么上面代碼改成下面這樣就不會(huì)有問題了:
def test(t=None):
if t is None:
t = []
t.append('test')
return t
print(test())
print(test())
- 可變參數(shù)
在Python函數(shù)中但校,還可以定義可變參數(shù)螃诅。顧名思義,可變參數(shù)就是傳入的參數(shù)個(gè)數(shù)是可變的状囱,可以是1個(gè)术裸、2個(gè)到任意個(gè),還可以是0個(gè)亭枷。
如何實(shí)現(xiàn)這個(gè)問題袭艺?我們想到的是list或者tuple來做參數(shù),如下程序:
def add(x=None):
if x is None:
x = []
sum = 0
for n in x:
sum = sum + n
return sum
print(add([1,2,3,4]))
上面程序可以簡(jiǎn)化一下叨粘,這么寫猾编,但是會(huì)讓調(diào)用者不太明白參數(shù)的類型:
def add(x):
sum = 0
for n in x:
sum = sum + n
return sum
print(add([1,2,3,4]))
下面瘤睹,我們利用Python的可變參數(shù)來寫:
# 在參數(shù)面前加入*
def add(*x):
sum = 0
for n in x:
sum = sum + n
return sum
# 在調(diào)用的時(shí)候,參數(shù)可以是任何類型答倡,參數(shù)個(gè)數(shù)也可以是隨意的了
print(add(1,2,3,4))
有這么一種情況轰传,你可以這么調(diào)用傳遞參數(shù):
def add(*x):
sum = 0
for n in x:
sum = sum + n
return sum
s = [1,2,3,4]
# Python允許你在list或tuple前面加一個(gè)*號(hào),把list或tuple的元素變成可變參數(shù)傳進(jìn)去:
print(add(*s))
- 關(guān)鍵字參數(shù)
可變參數(shù)在函數(shù)調(diào)用時(shí)自動(dòng)組裝為一個(gè)tuple瘪撇,關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動(dòng)組裝為一個(gè)dict获茬。
def infor(name,age,**other):
print('name=',name,'age=',age,'other=',other)
return
# 調(diào)用必填參數(shù)
infor('lisa',12)
# 傳任意個(gè)數(shù)的關(guān)鍵字參數(shù)
infor('ds',23,height = 176,city = 'hz')
# 傳一個(gè)dict的關(guān)鍵字參數(shù)
s = {'height' :'176','city' : 'hz'}
infor('ds',23,**s)
關(guān)鍵字參數(shù)有什么用?它可以擴(kuò)展函數(shù)的功能设江。比如锦茁,在infor函數(shù)里,我們保證能接收到name和age這兩個(gè)參數(shù)叉存,但是码俩,如果調(diào)用者愿意提供更多的參數(shù),我們也能收到歼捏。試想你正在做一個(gè)用戶注冊(cè)的功能稿存,除了用戶名和年齡是必填項(xiàng)外,其他都是可選項(xiàng)瞳秽,利用關(guān)鍵字參數(shù)來定義這個(gè)函數(shù)就能滿足注冊(cè)的需求瓣履。
- 命名關(guān)鍵字參數(shù)
在關(guān)鍵字參數(shù)中,如果我們要限定關(guān)鍵字的名字應(yīng)該如何辦练俐?Python提供了命名關(guān)鍵字參數(shù)袖迎。用法如下:
# 如果要限制關(guān)鍵字參數(shù)的名字,就可以用命名關(guān)鍵字參數(shù)腺晾,例如燕锥,只接收city和job作為關(guān)鍵字參數(shù)。
# 和關(guān)鍵字參數(shù)**other不同悯蝉,命名關(guān)鍵字參數(shù)需要一個(gè)特殊分隔符*归形,*后面的參數(shù)被視為命名關(guān)鍵字參數(shù)。這種方式定義的函數(shù)如下
def infor(name,age,*,height,city = 'hz'):
print(name,age,height,city)
return
# 注意鼻由,必須要寫上height與city暇榴,并且不可以拼錯(cuò),命名關(guān)鍵字參數(shù)可以有缺省值,從而簡(jiǎn)化調(diào)用
infor('lisa',12,height = '170',city = 'sh')
如果函數(shù)定義中已經(jīng)有了一個(gè)可變參數(shù)蕉世,后面跟著的命名關(guān)鍵字參數(shù)就不再需要一個(gè)特殊分隔符*了:
# 注意只能是可變參數(shù)蔼紧,不是關(guān)鍵字參數(shù),否則會(huì)報(bào)錯(cuò)
def infor(name,age,*other,height,city):
print(name,age,height,city)
return
# 注意狠轻,必須要寫上height與city奸例,并且不可以拼錯(cuò)
infor('lisa',12,height = '170',city = 'zh')
- 如何組合這些參數(shù)?
在Python中定義函數(shù)哈误,可以用必選參數(shù)哩至、默認(rèn)參數(shù)、可變參數(shù)蜜自、關(guān)鍵字參數(shù)和命名關(guān)鍵字參數(shù)菩貌,這5種參數(shù)都可以組合使用。但是請(qǐng)注意重荠,參數(shù)定義的順序必須是:必選參數(shù)箭阶、默認(rèn)參數(shù)、可變參數(shù)戈鲁、命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù)仇参。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
# 普通調(diào)用:
f1(1, 2)
# a = 1 b = 2 c = 0 args = () kw = {}
f1(1, 2, c=3)
# a = 1 b = 2 c = 3 args = () kw = {}
f1(1, 2, 3, 'a', 'b')
# a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
f1(1, 2, 3, 'a', 'b', x=99)
# a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
f2(1, 2, d=99, ext=None)
# a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
# 通過tuple和dict來調(diào)用:
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
f1(*args, **kw)
# a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
args = (1, 2, 3)
kw = {'d': 88, 'x': '#'}
f2(*args, **kw)
# a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
所以,對(duì)于任意函數(shù)婆殿,都可以通過類似func(*args, **kw)的形式調(diào)用它诈乒,無論它的參數(shù)是如何定義的。