在設計函數的時候,有時候我們能夠確認參數的個數,比如一個用來計算圓面積的函數,它所需要的參數就是半徑(πr^2),這個函數的參數是確定的。
> 你能不能寫一個能夠計算圓面積的函數呢?
然而,這個世界不總是這么簡單的,也不總是這么確定的,反而不確定性是這個世界常常存在的。如果看官了解量子力學——好多人聽都沒有聽過的東西——就更理解真正的不確定性了。當然,不用研究量子力學也一樣能夠體會到,世界充滿里了不確定性。不是嗎?塞翁失馬焉知非福,這不就是不確定性嗎?
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#參數收集)參數收集
既然有很多不確定性,那么函數的參數的個數,也當然有不確定性,函數怎么解決這個問題呢?python用這樣的方式解決參數個數的不確定性:
~~~
def func(x,*arg):
print x #輸出參數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) #賦給函數的參數個數不僅僅是2個
~~~
運行此代碼后,得到如下結果:
~~~
1 #這是函數體內的第一個print,參數x得到的值是1
(2, 3, 4, 5, 6, 7, 8, 9) #這是函數內的第二個print,參數arg得到的是一個元組
45 #最后的計算結果
~~~
從上面例子可以看出,如果輸入的參數個數不確定,其它參數全部通過*arg,以元組的形式由arg收集起來。對照上面的例子不難發現:
* 值1傳給了參數x
* 值2,3,4,5,6.7.8.9被塞入一個tuple里面,傳給了arg
為了能夠更明顯地看出*args(名稱可以不一樣,但是*符號必須要有),可以用下面的一個簡單函數來演示:
~~~
>>> def foo(*args):
... print args #打印通過這個參數得到的對象
...
~~~
下面演示分別傳入不同的值,通過參數*args得到的結果:
~~~
>>> 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'})
~~~
不管是什么,都一股腦地塞進了tuple中。
~~~
>>> foo("python")
('python',)
~~~
即使只有一個值,也是用tuple收集它。特別注意,在tuple中,如果只有一個元素,后面要有一個逗號。
還有一種可能,就是不給那個`*args`傳值,也是許可的。例如:
~~~
>>> def foo(x, *args):
... print "x:",x
... print "tuple:",args
...
>>> foo(7)
x: 7
tuple: ()
~~~
這時候`*args`收集到的是一個空的tuple。
> 在各類編程語言中,常常會遇到以foo,bar,foobar等之類的命名,不管是對變量、函數還是后面要講到的類。這是什么意思呢?下面是來自維基百科的解釋。
>
> 在計算機程序設計與計算機技術的相關文檔中,術語foobar是一個常見的無名氏化名,常被作為“偽變量”使用。
>
> 從技術上講,“foobar”很可能在1960年代至1970年代初通過迪吉多的系統手冊傳播開來。另一種說法是,“foobar”可能來源于電子學中反轉的foo信號;這是因為如果一個數字信號是低電平有效(即負壓或零電壓代表“1”),那么在信號標記上方一般會標有一根水平橫線,而橫線的英文即為“bar”。在《新黑客辭典》中,還提到“foo”可能早于“FUBAR”出現。
>
> 單詞“foobar”或分離的“foo”與“bar”常出現于程序設計的案例中,如同Hello World程序一樣,它們常被用于向學習者介紹某種程序語言。“foo”常被作為函數/方法的名稱,而“bar”則常被用作變量名。
除了用*args這種形式的參數接收多個值之外,還可以用**kargs的形式接收數值,不過這次有點不一樣:
~~~
>>> def foo(**kargs):
... print kargs
...
>>> foo(a=1,b=2,c=3) #注意觀察這次賦值的方式和打印的結果
{'a': 1, 'c': 3, 'b': 2}
~~~
如果這次還用foo(1,2,3)的方式,會有什么結果呢?
~~~
>>> foo(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 0 arguments (3 given)
~~~
如果用`**kargs`的形式收集值,會得到dict類型的數據,但是,需要在傳值的時候說明“鍵”和“值”,因為在字典中是以鍵值對形式出現的。
看官到這里可能想了,不是不確定性嗎?我也不知道參數到底會可能用什么樣的方式傳值呀,這好辦,把上面的都綜合起來。
~~~
>>> 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了,這樣就能夠足以應付各種各樣的參數要求了。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#另外一種傳值方式)另外一種傳值方式
~~~
>>> def add(x,y):
... return x + y
...
>>> add(2,3)
5
~~~
這是通常的函數調用方法,在前面已經屢次用到。這種方法簡單明快,很容易理解。但是,世界總是多樣性的,有時候你秀出下面的方式,甚至在某種情況用下面的方法可能更優雅。
~~~
>>> bars = (2,3)
>>> add(*bars)
5
~~~
先把要傳的值放到元組中,賦值給一個變量`bars`,然后用`add(*bars)`的方式,把值傳到函數內。這有點像前面收集參數的逆過程。注意的是,元組中元素的個數,要跟函數所要求的變量個數一致。如果這樣:
~~~
>>> 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)
~~~
就報錯了。
這是使用一個星號`*`,是以元組形式傳值,如果用`**`的方式,是不是應該以字典的形式呢?理當如此。
~~~
>>> 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
~~~
這種調用函數傳值的方式,至少在我的編程實踐中,用的不多。不過,不代表讀者不用。這或許是習慣問題。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#復習)復習
python中函數的參數通過賦值的方式來傳遞引用對象。下面總結通過總結常見的函數參數定義方式,來理解參數傳遞的流程。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#def-foop1p2p3)def foo(p1,p2,p3,...)
這種方式最常見了,列出有限個數的參數,并且彼此之間用逗號隔開。在調用函數的時候,按照順序以此對參數進行賦值,特備注意的是,參數的名字不重要,重要的是位置。而且,必須數量一致,一一對應。第一個對象(可能是數值、字符串等等)對應第一個參數,第二個對應第二個參數,如此對應,不得偏左也不得偏右。
~~~
>>> def foo(p1,p2,p3):
... print "p1==>",p1
... print "p2==>",p2
... print "p3==>",p3
...
>>> foo("python",1,["qiwsir","github","io"]) #一一對應地賦值
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) #注意看報錯信息
>>> 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個參數,實際上放置了4個,報錯
~~~
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#def-foop1value1p2value2)def foo(p1=value1,p2=value2,...)
這種方式比前面一種更明確某個參數的賦值,貌似這樣就不亂子了,很明確呀。頗有一個蘿卜對著一個坑的意味。
還是上面那個函數,用下面的方式賦值,就不用擔心順序問題了。
~~~
>>> foo(p3=3,p1=10,p2=222)
p1==> 10
p2==> 222
p3==> 3
~~~
也可以采用下面的方式定義參數,給某些參數有默認的值
~~~
>>> def foo(p1,p2=22,p3=33): #設置了兩個參數p2,p3的默認值
... print "p1==>",p1
... print "p2==>",p2
... print "p3==>",p3
...
>>> foo(11) #p1=11,其它的參數為默認賦值
p1==> 11
p2==> 22
p3==> 33
>>> foo(11,222) #按照順序,p2=222,p3依舊維持原默認值
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沒有默認值,必須要賦值的,否則報錯
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes at least 1 argument (1 given)
~~~
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#def-fooargs)def foo(*args)
這種方式適合于不確定參數個數的時候,在參數args前面加一個*,注意,僅一個喲。
~~~
>>> def foo(*args): #接收不確定個數的數據對象
... print args
...
>>> foo("qiwsir.github.io") #以tuple形式接收到,哪怕是一個
('qiwsir.github.io',)
>>> foo("qiwsir.github.io","python")
('qiwsir.github.io', 'python')
~~~
#### [](https://github.com/qiwsir/StarterLearningPython/blob/master/203.md#def-fooargs-1)def foo(**args)
這種方式跟上面的區別在于,必須接收類似arg=val形式的。
~~~
>>> def foo(**args): #這種方式接收,以dictionary的形式接收數據對象
... print args
...
>>> foo(1,2,3) #這樣就報錯了
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) #這樣就可以了,因為有了鍵值對
{'a': 1, 'c': 3, 'b': 2}
~~~
下面來一個綜合的,看看以上四種參數傳遞方法的執行順序
~~~
>>> 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'}
~~~
- 第零章 預備
- 關于Python的故事
- 從小工到專家
- Python安裝
- 集成開發環境
- 第壹章 基本數據類型
- 數和四則運算
- 除法
- 常用數學函數和運算優先級
- 寫一個簡單的程序
- 字符串(1)
- 字符串(2)
- 字符串(3)
- 字符串(4)
- 字符編碼
- 列表(1)
- 列表(2)
- 列表(3)
- 回顧list和str
- 元組
- 字典(1)
- 字典(2)
- 集合(1)
- 集合(2)
- 第貳章 語句和文件
- 運算符
- 語句(1)
- 語句(2)
- 語句(3)
- 語句(4)
- 語句(5)
- 文件(1)
- 文件(2)
- 迭代
- 練習
- 自省
- 第叁章 函數
- 函數(1)
- 函數(2)
- 函數(3)
- 函數(4)
- 函數練習
- 第肆章 類
- 類(1)
- 類(2)
- 類(3)
- 類(4)
- 類(5)
- 多態和封裝
- 特殊方法(1)
- 特殊方法(2)
- 迭代器
- 生成器
- 上下文管理器
- 第伍章 錯誤和異常
- 錯誤和異常(1)
- 錯誤和異常(2)
- 錯誤和異常(3)
- 第陸章 模塊
- 編寫模塊
- 標準庫(1)
- 標準庫(2)
- 標準庫(3)
- 標準庫(4)
- 標準庫(5)
- 標準庫(6)
- 標準庫(7)
- 標準庫(8)
- 第三方庫
- 第柒章 保存數據
- 將數據存入文件
- mysql數據庫(1)
- MySQL數據庫(2)
- mongodb數據庫(1)
- SQLite數據庫
- 電子表格
- 第捌章 用Tornado做網站
- 為做網站而準備
- 分析Hello
- 用tornado做網站(1)
- 用tornado做網站(2)
- 用tornado做網站(3)
- 用tornado做網站(4)
- 用tornado做網站(5)
- 用tornado做網站(6)
- 用tornado做網站(7)
- 第玖章 科學計算
- 為計算做準備
- Pandas使用(1)
- Pandas使用(2)
- 處理股票數據
- 附:網絡文摘
- 如何成為Python高手
- ASCII、Unicode、GBK和UTF-8字符編碼的區別聯系