1、高階函數python
python中函數是一等對象(first class);函數也是對象,而且它能夠像普通對象同樣複製、做爲參數、做爲返回值。
編程
返回函數或者參數是函數的函數就是高階函數,也被稱爲函數式編程
bash
In [23]: def counter(base): ...: def inc(x=1): ...: nonlocal base ...: base += x ...: return base ...: return inc # 返回一個函數 ...: In [24]: inc = counter(3) In [25]: inc Out[25]: <function __main__.counter.<locals>.inc> In [26]: inc() Out[26]: 4 In [27]: inc() Out[27]: 5
內置函數sorted()的實現:閉包
In [48]: def sort(it, cmp=lambda a, b: a<b): ...: ret = [] ...: for x in it: ...: for i, e in enumerate(ret): ...: if cmp(x, e): ...: ret.insert(i, x) ...: break ...: else: ...: ret.append(x) ...: return ret ...: In [49]: sort([1, 3, 5, 2, 4, ]) Out[49]: [1, 2, 3, 4, 5] In [50]: sort([1, 3, 5, 2, 4, ], lambda a, b: a>b) Out[50]: [5, 4, 3, 2, 1]
總結:
app
函數做爲返回值經常使用於閉包的場景:須要封裝一些變量ssh
函數做爲參數經常使用於大多數邏輯固定,少部分邏輯不固定的場景ide
函數做爲參數,返回值也爲是函數:經常使用於做爲參數的函數在執行先後須要一些額外的操做(增長功能)函數式編程
2、裝飾器函數
一、裝飾器
工具
函數的參數是一個函數,返回值也是一個函數,就能夠做爲裝飾器
In [77]: def add(x, y): ...: return x + y ...: In [78]: add.__name__ Out[78]: 'add' In [80]: def logger(fn): ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} called'.format(fn.__name__)) ...: return ret ...: return wrap ...: In [81]: loged_add = logger(add) In [82]: loged_add(3, 5) call add add called Out[82]: 8
簡單、優雅一點:
In [90]: def add(x, y): ...: return x + y ...: In [91]: def logger(fn): ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} called'.format(fn.__name__)) ...: return ret ...: return wrap ...: In [92]: add = logger(add) # add變量名被從新賦值定義,賦值是先執行右邊的部分 In [93]: add(3, 4) call add add called Out[93]: 7
更簡單、優雅:
In [102]: def logger(fn): ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} called'.format(fn.__name__)) ...: return ret ...: return wrap ...: In [103]: @logger # @這是裝飾器的語法糖,至關於 add=logger(add)=wrp ...: def add(x, y): ...: return x + y ...: In [104]: add(1, 2) call add add called Out[104]: 3
這是logger就是裝飾器了
3、裝飾器的反作用
被裝飾的函數函數名發生了改變
In [73]: add.__name__ # 被裝飾的函數函數名改變了 Out[73]: 'wrap' In [74]: help(add) Help on function wrap in module __main__: wrap(*args, **kwargs)
python是自文檔語言:
In [78]: def fn(): ...: print('hello') ...: In [79]: help(fn) Help on function fn in module __main__: fn() (END) In [82]: def fn(): ...: '''this is fn''' ...: print('hello') ...: In [83]: help(fn) Help on function fn in module __main__: fn() this is fn (END) In [84]: fn.__doc__ Out[84]: 'this is fn' In [85]: fn.__name__ Out[85]: 'fn' In [86]: dir(fn) # 查看全部的屬性和方法 Out[86]: ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
解決辦法:
In [87]: def logger(fn): ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} clled'.format(fn.__name__)) ...: return ret ...: wrap.__name__ = fn.__name__ ...: wrap.__doc__ = fn.__doc__ ...: return wrap ...: In [88]: @logger ...: def add(x, y): ...: return x+y ...: In [89]: add.__name__ Out[89]: 'add' In [90]: add(1, 2) call add add clled Out[90]: 3
能夠將解決辦法寫成一個函數:
def copy_proprities(src, dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ # 這個函數能夠寫成裝飾器 In [91]: def copy_proprities(src): ...: def _copy(dst): ...: dst.__name__ = src.__name__ ...: dst.__doc__ = src.__doc__ ...: return _copy ...: # 在裝飾器函數中調用它 In [92]: def logger(fn): ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} clled'.format(fn.__name__)) ...: return ret ...: copy_proprities(fn)(wrap) ...: return wrap ...: In [93]: @logger ...: def add(x, y): ...: return x+y ...: In [94]: add.__name__ Out[94]: 'add' In [95]: add(1, 2) call add add clled Out[95]: 3
將解決方案寫成一個裝飾器:
In [102]: def copy_proprities(src): ...: def _copy(dst): ...: dst.__name__ = src.__name__ ...: dst.__doc__ = src.__doc__ ...: return dst ...: return _copy ...: In [103]: def logger(fn): ...: @copy_proprities(fn) ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} clled'.format(fn.__name__)) ...: return ret ...: return wrap ...: In [104]: @logger # add=logger(fn)=wrap ...: def add(x, y): ...: return x+y ...: In [105]: add.__name__ Out[105]: 'add' In [106]: add(1,3) call add add clled Out[106]: 4
實際上標準中已經實現了這個功能:
In [119]: import functools In [120]: def logger(fn): ...: @functools.wraps(fn) ...: def wrap(*args, **kwargs): ...: print('call {}'.format(fn.__name__)) ...: ret = fn(*args, **kwargs) ...: print('{} clled'.format(fn.__name__)) ...: return ret ...: return wrap ...: In [121]: @logger ...: def add(x, y): ...: return x+y ...: In [122]: add.__name__ Out[122]: 'add'
4、類型提示
類型提示是python3新增的功能
python是動態類型語言,一個變量的類型,是在代碼運行時決定的
一個變量的類型在應用的生命週期中是可變的
In [4]: a = 1 In [5]: type(a) Out[5]: int In [6]: a = 'xxj' In [7]: type(a) Out[7]: str
在函數中:
In [8]: def add(x, y): ...: return x+y ...: # 對於這個函數咱們可能會這樣調用,由於咱們不知道它須要的參數類型 In [9]: add("a", "b") Out[9]: 'ab' In [10]: add(1, 2) Out[10]: 3 In [11]: add('a', 2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-11-fc7c9413859a> in <module>() ----> 1 add('a', 2) <ipython-input-8-aa3fbbe3d526> in add(x, y) 1 def add(x, y): ----> 2 return x+y TypeError: must be str, not int In [12]: help(add) # 幫助文檔中也沒有說明須要什麼類型的參數 Help on function add in module __main__: add(x, y) (END)
解決辦法:
In [15]: def add(x, y): ...: '''x + y ...: @param x int ...: @param y int ...: @return int ...: ''' ...: return x + y ...: In [17]: help(add) Help on function add in module __main__: add(x, y) x + y @param x int @param y int @return int (END)
這裏可能會有以下問題:
並非每一個人都會記得寫文檔
文檔並不能保證和代碼一塊兒更新
文檔是天然語言,不方便機器操做
因此在Python3中新增了類型提示這個特性:
In [18]: def add(x: int, y:int) -> int: ...: return x + y ...: In [19]: add(1, 2) Out[19]: 3 In [20]: help(add) Help on function add in module __main__: add(x:int, y:int) -> int (END) In [31]: add.__annotations__ Out[31]: {'return': int, 'x': int, 'y': int}
類型提示(類型註解),只是一個註釋,不會做任何檢查
In [22]: add('a', 'b') # 不檢查,不轉換;就只是一個註釋 Out[22]: 'ab'
但在一些IDE工具中會有警告信息
因此類型註解能夠提供給第三方工具使用(IDE, 靜態分析),或者在運行時獲取信息
在python3.5以前,類型註解只能用在函數參數和返回值