概覽
????函數(shù)在python里是作為第一類對象(First Class Objects)。在編程語言理論里,定義一個(gè)“第一類對象”作為編程對象能夠做到以下幾點(diǎn):
- 在運(yùn)行時(shí)創(chuàng)建
- 可以賦值給變量或者數(shù)據(jù)結(jié)構(gòu)里的元素
- 能作為參數(shù)傳遞給函數(shù)
- 可以作為函數(shù)的返回值
Treating a Function Like an Object
????在python里函數(shù)就是對象,類型是function。
>>> def factorial(n):
... '''returns n!'''
... return 1 if n < 2 else n * factorial(n-1)
...
>>> factorial(10)
3628800
>>> factorial.__doc__
'returns n!'
>>> type(factorial)
<class 'function'>
>>> fact = factorial
>>> fact
<function factorial at 0x1053fcf28>
>>> fact(5)
120
>>> map(factorial, range(4))
<map object at 0x10566b160>
>>> list(map(fact, range(4)))
[1, 1, 2, 6]
????上面的例子展示了函數(shù)作為第一類對象的特性。可以把函數(shù)賦值給一個(gè)對象,并且使用這個(gè)對象的名字調(diào)用函數(shù)。也可以把函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù)。
Higher-Order Functions
????一個(gè)函數(shù)把函數(shù)作為參數(shù)或者把函數(shù)作為返回結(jié)果就被稱為高階函數(shù)(high-order function)。
????函數(shù)式編程通常提供一些通用的高階函數(shù)如map,reduce,filter等。不過在python里由于list comprehensions還有g(shù)enerator expressions的引入,都不太重要了。在python3里map和filter返回生成器,而在python2里返回list。在python3里reduce被從built-in里移除,放在了functools模塊里。
Anonymous Functions
????在使用高階函數(shù)時(shí),有時(shí)候創(chuàng)建一個(gè)小的,一次性的函數(shù)會(huì)很方便,這個(gè)就是匿名函數(shù)的由來。
????匿名函數(shù)是由lambda關(guān)鍵字創(chuàng)建。不過由于匿名函數(shù)句法的局限,匿名函數(shù)的函數(shù)體只能是純表達(dá)式。除了作為高階函數(shù)的參數(shù)以為,匿名函數(shù)的使用場景非常有限。
The Seven Flavors of Callable Objects
????call操作符(就是())除了用戶自定義函數(shù)以外,也可以用在其他對象上。要確定一個(gè)函數(shù)是否是callable的,可以使用內(nèi)建的callable() 函數(shù)。python定義了七種callable類型:
- User-defined functions:使用def語句或者用lambda表達(dá)式創(chuàng)建。
- Built-in functions:使用C實(shí)現(xiàn)的函數(shù),如len。
- Built-in methods:使用C實(shí)現(xiàn)的方法,如dict.get。
- Methods:定義在類里的函數(shù)。
- Classes:調(diào)用類就是創(chuàng)建一個(gè)實(shí)例。
- Class instances:類如果定義了
__call__方法,那么類的實(shí)例也可以像函數(shù)一樣調(diào)用。 - Generator functions:使用
yield關(guān)鍵字的函數(shù)或方法。
User-Defined Callable Types
????這一節(jié)介紹的就是7種callabe類型里的第6種。主要使用的場景就是創(chuàng)建在調(diào)用時(shí)需要保存內(nèi)部狀態(tài)的像函數(shù)一樣的對象,另一種方式就是閉包(Closures),后面會(huì)詳細(xì)講。
From Positional to Keyword-Only Parameters
????python函數(shù)最棒的特性就是極度靈活的參數(shù)處理方式,在python進(jìn)一步增強(qiáng)提供了僅限關(guān)鍵字參數(shù)(keyword-only)。與之相關(guān)的就是當(dāng)調(diào)用一個(gè)函數(shù)時(shí),使用和*去展開iterables和mappings到不同的參數(shù)。
>>>def tag(name, *content, cls=None, **attrs):
"""Generate one or more HTML tags"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(' %s="%s"' % (attr, value)
for attr, value
in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join('<%s%s>%s</%s>' %
(name, attr_str, c, name) for c in content)
else:
return '<%s%s />' % (name, attr_str)
>>> tag('br')
'<br />'
>>> tag('p', 'hello')
'<p>hello</p>'
>>> print(tag('p', 'hello', 'world'))
<p>hello</p>
<p>world</p>
>>> tag('p', 'hello', id=33)
'<p id="33">hello</p>'
>>> print(tag('p', 'hello', 'world', cls='sidebar'))
<p class="sidebar">hello</p>
<p class="sidebar">world</p>
>>> tag(content='testing', name="img")
'<img content="testing" />'
>>> my_tag = {'name': 'img', 'title': 'Sunset Boulevard',
... 'src': 'sunset.jpg', 'cls': 'framed'}
>>> tag(**my_tag)
'<img class="framed" src="sunset.jpg" title="Sunset Boulevard" />'
????僅限關(guān)鍵字參數(shù)(keyword-only)是python3里的新特性。參數(shù)只能作為關(guān)鍵字參數(shù),而不能從未命名的位置參數(shù)捕獲。要定義一個(gè)僅限關(guān)鍵字參數(shù),就把他放在前綴的參數(shù)后面。如果不需要變化的參數(shù),但是還需要僅限關(guān)鍵字參數(shù),那么就直接放在的后面。
Retrieving Information About Parameters
????函數(shù)對象有幾個(gè)內(nèi)省的屬性存儲(chǔ)了參數(shù)信息:
-
__defaults__:存儲(chǔ)了參數(shù)的默認(rèn)值 -
__kwdefaults__:存儲(chǔ)了keyword-only參數(shù)的默認(rèn)值 -
__code__:__code__.co_varnames存儲(chǔ)了參數(shù)名和局部變量的名字,通過__code__.co_argcount定位前N個(gè)作為參數(shù)。
????更簡單的是使用inspect.signature來獲取參數(shù)信息。
Function Annotations
????python3提供了語法給函數(shù)的參數(shù)和返回值附加一些元數(shù)據(jù)。如類型還有限制條件,不過現(xiàn)在python只是做了些記錄,并沒有去做檢查。
Packages for Functional Programming
????operator包里包含了很多算術(shù)操作符,另外幾個(gè)比較有用的函數(shù)itemgetter,attrgetter,methodcaller。
????functools包里主要介紹了partial,可以用來生成一個(gè)新的callable其中的一些參數(shù)值固定。
>>> a = range(10)
>>> from operator import itemgetter
>>> a_itemgetter = itemgetter(5, 1)
>>> a_itemgetter(a)
(5, 1)
>>> from collections import namedtuple
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> latlon = LatLong(30, 140)
>>> from operator import attrgetter
>>> attr_lat = attrgetter('lat')
>>> attr_lat(latlon)
30
>>> from operator import methodcaller
>>> s = 'The time has come'
>>> upcase = methodcaller('upper')
>>> upcase(s)
'THE TIME HAS COME'
>>> from operator import mul
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(7)
21