1、函數
python
一、函數編程
函數是python中組織代碼的最小單元
bash
函數是實現模塊化編程的基本組件ide
Python使用def語句定義函數模塊化
每一個Python函數都有一個返回值,默認爲None,也可使用「return value」明肯定定義返回值函數
def語句會建立一個函數對象,並同時建立一個指向函數的對象引用測試
函數也是對象,能夠存儲在組合數據類型中,也能夠做爲參數傳遞給其它函數ui
callable()可用於測試對象是否可調用spa
函數經過def定義,接着函數名,函數名後面用一對小括號列出參數列表,使用一個冒號開始函數體。orm
函數體是正常的Python語句,能夠組合任意結構
return語句表示函數的返回值
函數有輸入(參數)和輸出(返回值),函數實際上是一個代碼單元,把輸入轉化爲輸出。
定義函數的時候並不會執行函數體,當調用函數時纔會執行函數體
函數經過函數名來調用,函數名後面一對小括號裏面傳入實參
In [2]: def f1(): ...: print("hello") ...: In [3]: f1 Out[3]: <function __main__.f1> In [4]: f1() hello In [5]: a = f1 In [6]: b = f1() hello In [7]: a Out[7]: <function __main__.f1> In [8]: b In [9]: type(a) Out[9]: function In [10]: type(b) Out[10]: NoneType In [15]: callable(a) # 實現了__call__()方法就可調用 Out[15]: True
二、對於Python的函數,咱們須要記住的是
1)函數的默認返回值是None。
2)python是一個自上而下逐行解釋並執行的語言。所以,函數的定義必須在函數被調用以前。同名的函數,後定義的會覆蓋前面定義的。
3)程序執行的時候,遇到函數定義只會先將函數總體讀進內存,並不馬上執行。等到函數被調用的時候才執行函數體。
4)python函數的參數傳遞的是值傳遞仍是引用傳遞。
函數參數傳遞本質上和變量總體複製同樣,只是兩個變量分別爲形參a和實參b。那麼,a=b後,a變了,b值是否跟着變呢?這取決於對象內容可變不可變
值傳遞:
指在調用函數時,將實際參數複製一份傳遞給函數,函數對參數進行修改將不會影響到實際參數
適用於不可變對象(如int, str,tuples等)做爲參數傳遞時,例如元組
引用傳遞:
指調用函數時,將實際參數的地址傳遞給函數,函數對參數進行修改,將影響實際參數
適用於可變對象(如list,dict,類的實例等)做爲參數傳遞時,例如列表
淺複製:(也叫影子複製)
只複製父對象,不會複製對象的內部的子對象
深複製:
複製對象及其子對象
賦值是引用傳遞
In [74]: l1 = [1, 2, 3] In [75]: l2 = l1 In [76]: l2 Out[76]: [1, 2, 3] In [77]: l3 = l1.copy() In [78]: l3 Out[78]: [1, 2, 3] In [79]: id(l3) # l3和l1應用的是不一樣內存對象 Out[79]: 140149284135624 In [80]: id(l1) Out[80]: 140149295996616 In [81]: id(l2) # l2和l1引用的是同一個內存對象 Out[81]: 140149295996616
2、函數的參數
對於函數,最重要的知識點莫過於參數了。
參數分爲形式參數(形參)和實際參數(實參)。
def f1(a, b, c): pass f1(1, 2, 3)
其中,a,b,c就是形參,1,2,3就是實參,也就是實際要傳遞的參數。
In [27]: def add(x, y): ...: print(x + y) ...: return x + y ...: In [28]: add(3, 5) 8 Out[28]: 8 In [29]: add(3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-29-c9889e45a711> in <module>() ----> 1 add(3) TypeError: add() missing 1 required positional argument: 'y' In [30]: add(3, 5, 8) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-30-0b52ebc12bc6> in <module>() ----> 1 add(3, 5, 8) TypeError: add() takes 2 positional arguments but 3 were given In [42]: add(3, "5") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-42-d73b87d3f8c1> in <module>() ----> 1 add(3, "5") <ipython-input-31-4950d8722875> in add(x, y) 1 def add(x, y): ----> 2 ret = x + y 3 print('{} + {} = {}'.format(x, y, ret)) 4 return ret TypeError: unsupported operand type(s) for +: 'int' and 'str'
函數調用時,傳入的實參必須和函數定義時的行參想匹配,若是不匹配會拋出TypeError.
Python中的形式參數有如下幾種:
一、位置參數
一般在傳遞參數的時候咱們按照參數的位置,逐一傳遞,這叫「位置參數」。
In [31]: def add(x, y): ...: ret = x + y ...: print('{} + {} = {}'.format(x, y, ret)) ...: return ret ...: In [32]: add(3, 5) 3 + 5 = 8 Out[32]: 8
二、關鍵字參數
而有時候咱們會用「形參名」=「值」的方式傳遞參數,這叫「關鍵字參數或指定參數」。
In [33]: add(x=3, y=5) 3 + 5 = 8 Out[33]: 8 In [34]: add(y=3, x=5) # 關鍵字參數和順序無關 5 + 3 = 8 Out[34]: 8
位置參數和關鍵字參數混用時關鍵字參數必須在位置參數後面
In [37]: add(3, y=5) 3 + 5 = 8 Out[37]: 8 In [38]: add(x=3, 5) File "<ipython-input-38-165b39de39ac>", line 1 add(x=3, 5) ^ SyntaxError: positional argument follows keyword argument In [39]: add(3, x=5) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-39-b0f1746a3413> in <module>() ----> 1 add(3, x=5) TypeError: add() got multiple values for argument 'x'
三、默認參數
默認參數是爲某些參數設定一個默認值,既能夠減小參數傳入量,也能夠享受使用默認值的便利。
默認參數必須位於參數列表的最後部分!
默認參數是在函數定義時,指的是形參!
In [46]: def inc(base, x=1): ...: return base + x ...: In [47]: inc(3, 2) Out[47]: 5 In [48]: inc(3) Out[48]: 4 In [49]: inc(3, x=4) Out[49]: 7 In [56]: inc(base=4) Out[56]: 5 In [57]: inc(base=4, x=5) Out[57]: 9 In [50]: def inc(x=1, base): ...: return base + x ...: File "<ipython-input-50-ac010ba50fd9>", line 1 def inc(x=1, base): ^ SyntaxError: non-default argument follows default argument
四、可變(動態)參數
Python的動態參數有兩種,分別是*args和**kwargs,這裏面的關鍵是一個和兩個星號,而不是args和kwargs,實際上你可使用*any或**whatever的方式,但就如self同樣,潛規則咱們使用*args和**kwargs。
可變參數是在函數定義時,指的是形參!
*args:位置可變參數;函數定義時參數名前加一個星號
一個星號表示接受任意個動態參數。調用時,會將實際參數打包成一個元組傳入函數。
此時只能經過位置參數傳參
In [63]: def sum(*args): ...: ret = 0 ...: for i in args: ...: ret += i ...: return ret ...: In [64]: sum() Out[64]: 0 In [65]: sum(1) Out[65]: 1 In [66]: sum(1, 2, 3, 8) Out[66]: 14
**kwargs:關鍵字可變參數;函數定義時參數名前加兩個星號
兩個星表示接受鍵值對的動態參數,數量任意。調用的時候會將實際參數打包成字典。
此時只能經過關鍵字參數傳參
例如:
In [73]: def connect(**kwargs): ...: print(type(kwargs)) ...: for k, v in kwargs.items(): ...: print('{} => {}'.format(k, v)) ...: In [74]: connect(host="localhost", port=3300) <class 'dict'> host => localhost port => 3300 In [79]: connect("localhost", port=3300) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-79-6aeadd0e08a5> in <module>() ----> 1 connect("localhost", port=3300) TypeError: connect() takes 0 positional arguments but 1 was given
萬能參數:
當*args和**kwargs組合起來使用,理論上能接受任何形式和數量的參數,在不少代碼中咱們都能見到這種定義方式。須要注意的是,*args必須出如今**kwargs以前。
In [83]: def fn(*args, **kwargs): ...: print(args) ...: print(kwargs) ...: In [84]: fn(2, 3, 5, x=11, y="xxj") (2, 3, 5) {'x': 11, 'y': 'xxj'} In [85]: def fn(**kwargs, *args): ...: print(args) ...: print(kwargs) File "<ipython-input-85-ae6e58836952>", line 1 def fn(**kwargs, *args): ^ SyntaxError: invalid syntax
可變參數和普通參數混合使用:
In [91]: def fn(x, y, *args, **kwargs): ...: print(x) ...: print(y) ...: print(args) ...: print(kwargs) ...: In [92]: fn(2, 3, 4, 5, 6, a=1,b=2) 2 3 (4, 5, 6) {'a': 1, 'b': 2} In [95]: fn(2, 3) 2 3 () {} In [97]: fn(2, y=3) 2 3 () {} In [99]: fn(2, 3, 4) 2 3 (4,) {} In [100]: fn(2, 3, a=23) 2 3 () {'a': 23} In [107]: def fn(*args, x): # 位置可變參數是否能夠在普通參數以前 ...: print(args) ...: print(x) ...: In [108]: fn(1, 2, 3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-108-9ab6b0a400c3> in <module>() ----> 1 fn(1, 2, 3) TypeError: fn() missing 1 required keyword-only argument: 'x' In [109]: fn(1, 2, x=3) (1, 2) 3 In [110]: fn(1, x=3) (1,) 3 In [111]: fn(x=3) () 3 In [112]: def fn(**kwargs, x): # 關鍵字可變參數是否能夠在普通參數以前 ...: print(kwargs) ...: print(x) ...: File "<ipython-input-112-3226e57525e0>", line 1 def fn(**kwargs, x): ^ SyntaxError: invalid syntax
位置可變參數也能夠在普通參數以前,可是在位置可變參數以後的普通參數變成了keyword-only參數(只能以關鍵字參數登入)
關鍵字可變參數不能夠在普通參數以前(爲何?)
可變參數和默認參數混合使用:
In [113]: def fn(x=5, *args): ...: print(x) ...: print(args) ...: In [114]: fn(1) 1 () In [115]: fn(1,2) 1 (2,) In [116]: fn(1,2,3,4) 1 (2, 3, 4) In [117]: fn(x=3, 4) File "<ipython-input-117-2d95cecddace>", line 1 fn(x=3, 4) ^ SyntaxError: positional argument follows keyword argument In [121]: def fn(*args, x=5): # 位置參數在默認參數以前沒限制 ...: print(x) ...: print(args) ...: In [122]: fn() 5 () In [123]: fn(1, 2) 5 (1, 2) In [124]: fn(1) 5 (1,) In [125]: fn(1, x=3) 3 (1,) In [127]: def fn(**kwargs, x=5): ...: print(x) ...: print(kwargs) ...: File "<ipython-input-127-069a751edec8>", line 1 def fn(**kwargs, x=5): ^ SyntaxError: invalid syntax In [128]: def fn(x=5, **kwargs): ...: print(x) ...: print(kwargs) ...: ...: In [129]: fn() 5 {} In [130]: fn(1) 1 {} In [131]: fn(1, a=2) 1 {'a': 2} In [132]: fn(x=1, a=2) 1 {'a': 2}
可變位置參數在默認參數以後,默認參數不能使用關鍵字傳參
可變關鍵字參數不能在默認參數以前
當默認參數和可變參數一塊兒出現時,默認參數至關於普通參數
小結:
函數的參數規則這麼多,頭都大了;這裏咱們建議的函數參數使用用法:
1)默認參數靠後
2)可變參數靠後
3)默認參數和可變參數不一樣時出現
不遵照不必定錯,遵照代碼可讀性高
當咱們須要同時使用默認參數和可變參數時怎麼辦?
咱們一般這樣處理:
In [139]: def connect(host='127.0.0.1', port=3306, user='root', password='', **kwargs): ...: pass ...: In [140]: def connect(**kwargs): ...: host = kwargs.pop('host', '127.0.0.1') ...:
3、參數解構
一、參數解構
調用函數時,傳入實參時加一個星號,能夠把iterable解構成位置參數
調用函數時,傳入實參時加兩個個星號,能夠把dict解構成關鍵字參數
In [141]: def add(x, y): ...: ret = x + y ...: print('{} + {} = {}'.format(x, y, ret)) ...: In [142]: add(1, 2) 1 + 2 = 3 In [145]: t = [1, 2] In [146]: add(*t) 1 + 2 = 3 In [143]: t = [1, 2, 3] In [144]: add(*t) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-144-d90b03560d6e> in <module>() ----> 1 add(*t) TypeError: add() takes 2 positional arguments but 3 were given In [147]: t = [(1, 2), (3, 4)] In [148]: add(*t) In [148]: add(*t) (1, 2) + (3, 4) = (1, 2, 3, 4) # 字典解構成關鍵字參數 In [153]: d = {'x':1, 'y':2} In [154]: add(**d) 1 + 2 = 3 In [155]: add(*d) x + y = xy
二、參數解構和可變參數混用
In [161]: def sum(*args): ...: ret = 0 ...: for i in args: ...: ret += i ...: return ret ...: In [162]: sum(*(1, 2)) Out[162]: 3 In [163]: sum(*[1, 2]) Out[163]: 3 In [165]: sum(*range(5)) Out[165]: 10
三、參數解構的限制
關鍵字參數解構,key必須是str
In [167]: def fn(**kwargs): ...: print(kwargs) ...: In [168]: fn(**{'a':1}) {'a': 1} In [170]: fn(**{12:1}) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-170-99986e5a722a> in <module>() ----> 1 fn(**{12:1}) In [211]: def fn(*args, **kwargs): ...: print(args) ...: print(kwargs) ...: In [212]: fn(*[1, 2, 3]) (1, 2, 3) {} In [214]: fn(*[1, 2, 3], **{'a':1, 'b':2}) (1, 2, 3) {'a': 1, 'b': 2}
四、keyword-only參數
星號以後的參數只能經過關鍵字參數傳入
可變位置參數以後的參數也是keyword-only參數
只能經過關鍵字參數傳入的參數就交keyword-only參數
keyword-only參數能夠有默認值
In [179]: def fn(*, x): ...: print(x) ...: In [180]: fn(1) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-180-cb5d79cf2c77> in <module>() ----> 1 fn(1) TypeError: fn() takes 0 positional arguments but 1 was given In [181]: fn(x=1) 1 In [182]: fn(1, x=2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-182-8eb9b719c886> in <module>() ----> 1 fn(1, x=2) TypeError: fn() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given In [183]:
keyword-only參數與其它參數混用:
In [188]: def fn(x, *, y): ...: print(x) ...: print(y) ...: In [189]: fn(1, y=2) 1 2 In [193]: def fn(x=1, *, y=2): ...: print(x) ...: print(y) ...: ...: In [194]: fn() 1 2 In [199]: def fn(x=1, *, y): # ...: print(x) ...: print(y) ...: ...: In [200]: fn(y=3) 1 3 In [201]: fn(3) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-201-f005f2a6106f> in <module>() ----> 1 fn(3) TypeError: fn() missing 1 required keyword-only argument: 'y' In [202]: In [205]: def fn(*, x, y): # *號後能夠有多個keyword-only參數 ...: print(x) ...: print(y) ...: In [206]: fn(x=1, y=2) 1 2 In [207]: def fn(x, y, *): # *號不能寫在最後 ...: print(x) ...: print(y) ...: File "<ipython-input-207-6c060b52ac76>", line 1 def fn(x, y, *): ^ SyntaxError: named arguments must follow bare *