在設(shè)計(jì)函數(shù)的時(shí)候,有時(shí)候我們能夠確認(rèn)參數(shù)的個(gè)數(shù),比如一個(gè)用來計(jì)算圓面積的函數(shù),它所需要的參數(shù)就是半徑(πr^2),這個(gè)函數(shù)的參數(shù)是確定的。
你能不能寫一個(gè)能夠計(jì)算圓面積的函數(shù)呢?
然而,這個(gè)世界不總是這么簡單的,也不總是這么確定的,反而不確定性是這個(gè)世界常常存在的。如果看官了解量子力學(xué)——好多人聽都沒有聽過的東西——就更理解真正的不確定性了。當(dāng)然,不用研究量子力學(xué)也一樣能夠體會(huì)到,世界充滿里了不確定性。不是嗎?塞翁失馬焉知非福,這不就是不確定性嗎?
既然有很多不確定性,那么函數(shù)的參數(shù)的個(gè)數(shù),也當(dāng)然有不確定性,函數(shù)怎么解決這個(gè)問題呢?python用這樣的方式解決參數(shù)個(gè)數(shù)的不確定性:
def func(x,*arg):
print x #輸出參數(shù)x的值
result = x
print arg #輸出通過*arg方式得到的值
for i in arg:
result +=i
return result
print func(1,2,3,4,5,6,7,8,9) #賦給函數(shù)的參數(shù)個(gè)數(shù)不僅僅是2個(gè)
運(yùn)行此代碼后,得到如下結(jié)果:
1 #這是函數(shù)體內(nèi)的第一個(gè)print,參數(shù)x得到的值是1
(2, 3, 4, 5, 6, 7, 8, 9) #這是函數(shù)內(nèi)的第二個(gè)print,參數(shù)arg得到的是一個(gè)元組
45 #最后的計(jì)算結(jié)果
從上面例子可以看出,如果輸入的參數(shù)個(gè)數(shù)不確定,其它參數(shù)全部通過*arg,以元組的形式由arg收集起來。對照上面的例子不難發(fā)現(xiàn):
為了能夠更明顯地看出args(名稱可以不一樣,但是符號(hào)必須要有),可以用下面的一個(gè)簡單函數(shù)來演示:
>>> def foo(*args):
... print args #打印通過這個(gè)參數(shù)得到的對象
...
下面演示分別傳入不同的值,通過參數(shù)*args得到的結(jié)果:
>>> foo(1,2,3)
(1, 2, 3)
>>> foo("qiwsir","qiwsir.github.io","python")
('qiwsir', 'qiwsir.github.io', 'python')
>>> foo("qiwsir",307,["qiwsir",2],{"name":"qiwsir","lang":"python"})
('qiwsir', 307, ['qiwsir', 2], {'lang': 'python', 'name': 'qiwsir'})
不管是什么,都一股腦地塞進(jìn)了tuple中。
>>> foo("python")
('python',)
即使只有一個(gè)值,也是用tuple收集它。特別注意,在tuple中,如果只有一個(gè)元素,后面要有一個(gè)逗號(hào)。
還有一種可能,就是不給那個(gè)*args
傳值,也是許可的。例如:
>>> def foo(x, *args):
... print "x:",x
... print "tuple:",args
...
>>> foo(7)
x: 7
tuple: ()
這時(shí)候*args
收集到的是一個(gè)空的tuple。
在各類編程語言中,常常會(huì)遇到以foo,bar,foobar等之類的命名,不管是對變量、函數(shù)還是后面要講到的類。這是什么意思呢?下面是來自維基百科的解釋。
在計(jì)算機(jī)程序設(shè)計(jì)與計(jì)算機(jī)技術(shù)的相關(guān)文檔中,術(shù)語foobar是一個(gè)常見的無名氏化名,常被作為“偽變量”使用。
從技術(shù)上講,“foobar”很可能在1960年代至1970年代初通過迪吉多的系統(tǒng)手冊傳播開來。另一種說法是,“foobar”可能來源于電子學(xué)中反轉(zhuǎn)的foo信號(hào);這是因?yàn)槿绻粋€(gè)數(shù)字信號(hào)是低電平有效(即負(fù)壓或零電壓代表“1”),那么在信號(hào)標(biāo)記上方一般會(huì)標(biāo)有一根水平橫線,而橫線的英文即為“bar”。在《新黑客辭典》中,還提到“foo”可能早于“FUBAR”出現(xiàn)。
單詞“foobar”或分離的“foo”與“bar”常出現(xiàn)于程序設(shè)計(jì)的案例中,如同Hello World程序一樣,它們常被用于向?qū)W習(xí)者介紹某種程序語言。“foo”常被作為函數(shù)/方法的名稱,而“bar”則常被用作變量名。
除了用*args這種形式的參數(shù)接收多個(gè)值之外,還可以用**kargs的形式接收數(shù)值,不過這次有點(diǎn)不一樣:
>>> def foo(**kargs):
... print kargs
...
>>> foo(a=1,b=2,c=3) #注意觀察這次賦值的方式和打印的結(jié)果
{'a': 1, 'c': 3, 'b': 2}
如果這次還用foo(1,2,3)的方式,會(huì)有什么結(jié)果呢?
>>> foo(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 0 arguments (3 given)
如果用**kargs
的形式收集值,會(huì)得到dict類型的數(shù)據(jù),但是,需要在傳值的時(shí)候說明“鍵”和“值”,因?yàn)樵谧值渲惺且枣I值對形式出現(xiàn)的。
看官到這里可能想了,不是不確定性嗎?我也不知道參數(shù)到底會(huì)可能用什么樣的方式傳值呀,這好辦,把上面的都綜合起來。
>>> def foo(x,y,z,*args,**kargs):
... print x
... print y
... print z
... print args
... print kargs
...
>>> foo('qiwsir',2,"python")
qiwsir
2
python
()
{}
>>> foo(1,2,3,4,5)
1
2
3
(4, 5)
{}
>>> foo(1,2,3,4,5,name="qiwsir")
1
2
3
(4, 5)
{'name': 'qiwsir'}
很good了,這樣就能夠足以應(yīng)付各種各樣的參數(shù)要求了。
>>> def add(x,y):
... return x + y
...
>>> add(2,3)
5
這是通常的函數(shù)調(diào)用方法,在前面已經(jīng)屢次用到。這種方法簡單明快,很容易理解。但是,世界總是多樣性的,有時(shí)候你秀出下面的方式,甚至在某種情況用下面的方法可能更優(yōu)雅。
>>> bars = (2,3)
>>> add(*bars)
5
先把要傳的值放到元組中,賦值給一個(gè)變量bars
,然后用add(*bars)
的方式,把值傳到函數(shù)內(nèi)。這有點(diǎn)像前面收集參數(shù)的逆過程。注意的是,元組中元素的個(gè)數(shù),要跟函數(shù)所要求的變量個(gè)數(shù)一致。如果這樣:
>>> bars = (2,3,4)
>>> add(*bars)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: add() takes exactly 2 arguments (3 given)
就報(bào)錯(cuò)了。
這是使用一個(gè)星號(hào)*
,是以元組形式傳值,如果用**
的方式,是不是應(yīng)該以字典的形式呢?理當(dāng)如此。
>>> def book(author,name):
... print "%s is writing %s" % (author,name)
...
>>> bars = {"name":"Starter learning Python","author":"Kivi"}
>>> book(**bars)
Kivi is writing Starter learning Python
這種調(diào)用函數(shù)傳值的方式,至少在我的編程實(shí)踐中,用的不多。不過,不代表讀者不用。這或許是習(xí)慣問題。
python中函數(shù)的參數(shù)通過賦值的方式來傳遞引用對象。下面總結(jié)通過總結(jié)常見的函數(shù)參數(shù)定義方式,來理解參數(shù)傳遞的流程。
這種方式最常見了,列出有限個(gè)數(shù)的參數(shù),并且彼此之間用逗號(hào)隔開。在調(diào)用函數(shù)的時(shí)候,按照順序以此對參數(shù)進(jìn)行賦值,特備注意的是,參數(shù)的名字不重要,重要的是位置。而且,必須數(shù)量一致,一一對應(yīng)。第一個(gè)對象(可能是數(shù)值、字符串等等)對應(yīng)第一個(gè)參數(shù),第二個(gè)對應(yīng)第二個(gè)參數(shù),如此對應(yīng),不得偏左也不得偏右。
>>> def foo(p1,p2,p3):
... print "p1==>",p1
... print "p2==>",p2
... print "p3==>",p3
...
>>> foo("python",1,["qiwsir","github","io"]) #一一對應(yīng)地賦值
p1==> python
p2==> 1
p3==> ['qiwsir', 'github', 'io']
>>> foo("python")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 3 arguments (1 given) #注意看報(bào)錯(cuò)信息
>>> foo("python",1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 3 arguments (4 given) #要求3個(gè)參數(shù),實(shí)際上放置了4個(gè),報(bào)錯(cuò)
這種方式比前面一種更明確某個(gè)參數(shù)的賦值,貌似這樣就不亂子了,很明確呀。頗有一個(gè)蘿卜對著一個(gè)坑的意味。
還是上面那個(gè)函數(shù),用下面的方式賦值,就不用擔(dān)心順序問題了。
>>> foo(p3=3,p1=10,p2=222)
p1==> 10
p2==> 222
p3==> 3
也可以采用下面的方式定義參數(shù),給某些參數(shù)有默認(rèn)的值
>>> def foo(p1,p2=22,p3=33): #設(shè)置了兩個(gè)參數(shù)p2,p3的默認(rèn)值
... print "p1==>",p1
... print "p2==>",p2
... print "p3==>",p3
...
>>> foo(11) #p1=11,其它的參數(shù)為默認(rèn)賦值
p1==> 11
p2==> 22
p3==> 33
>>> foo(11,222) #按照順序,p2=222,p3依舊維持原默認(rèn)值
p1==> 11
p2==> 222
p3==> 33
>>> foo(11,222,333) #按順序賦值
p1==> 11
p2==> 222
p3==> 333
>>> foo(11,p2=122)
p1==> 11
p2==> 122
p3==> 33
>>> foo(p2=122) #p1沒有默認(rèn)值,必須要賦值的,否則報(bào)錯(cuò)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes at least 1 argument (1 given)
這種方式適合于不確定參數(shù)個(gè)數(shù)的時(shí)候,在參數(shù)args前面加一個(gè)*,注意,僅一個(gè)喲。
>>> def foo(*args): #接收不確定個(gè)數(shù)的數(shù)據(jù)對象
... print args
...
>>> foo("qiwsir.github.io") #以tuple形式接收到,哪怕是一個(gè)
('qiwsir.github.io',)
>>> foo("qiwsir.github.io","python")
('qiwsir.github.io', 'python')
這種方式跟上面的區(qū)別在于,必須接收類似arg=val形式的。
>>> def foo(**args): #這種方式接收,以dictionary的形式接收數(shù)據(jù)對象
... print args
...
>>> foo(1,2,3) #這樣就報(bào)錯(cuò)了
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 0 arguments (3 given)
>>> foo(a=1,b=2,c=3) #這樣就可以了,因?yàn)橛辛随I值對
{'a': 1, 'c': 3, 'b': 2}
下面來一個(gè)綜合的,看看以上四種參數(shù)傳遞方法的執(zhí)行順序
>>> def foo(x,y=2,*targs,**dargs):
... print "x==>",x
... print "y==>",y
... print "targs_tuple==>",targs
... print "dargs_dict==>",dargs
...
>>> foo("1x")
x==> 1x
y==> 2
targs_tuple==> ()
dargs_dict==> {}
>>> foo("1x","2y")
x==> 1x
y==> 2y
targs_tuple==> ()
dargs_dict==> {}
>>> foo("1x","2y","3t1","3t2")
x==> 1x
y==> 2y
targs_tuple==> ('3t1', '3t2')
dargs_dict==> {}
>>> foo("1x","2y","3t1","3t2",d1="4d1",d2="4d2")
x==> 1x
y==> 2y
targs_tuple==> ('3t1', '3t2')
dargs_dict==> {'d2': '4d2', 'd1': '4d1'}
更多建議: