文章目錄
- 1.functools
- 2.operator.itemgetter
- 3.operator.attrgetter
雖然 Guido 明確表明,Python 的目標不是變成函數式編程語言,但是得益于 operator 和
functools 等包的支持,函數式編程風格也可以信手拈來。接下來的兩節分別介紹這兩
個包。
1.functools
示例1 使用 reduce 函數和一個匿名函數計算階乘
from functools import reduce
def fact(n):
return reduce(lambda a, b: a*b, range(1, n+1))
operator 模塊為多個算術運算符提供了對應的函數,從而避免編寫 lambda a, b: a*b
這種平凡的匿名函數。使用算術運算符函數,可以把示例 5-21 改寫成示例 5-22 那樣。
示例 2使用 reduce 和 operator.mul 函數計算階乘:
from functools import reduce
from operator import mul
def fact(n):
return reduce(mul, range(1, n+1))
2.operator.itemgetter
operator 模塊中還有一類函數,能替代從序列中取出元素或讀取對象屬性的 lambda 表
達式:因此,itemgetter 和 attrgetter 其實會自行構建函數。
示例3 演示使用 itemgetter 排序一個元組列表
from operator import itemgetter
metro_data = [('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),]
for city in sorted(metro_data, key=itemgetter(1)):print(city)
itemgetter(1) 的作用與 lambda fields: fields[1] 一樣:創建一個接受集合
的函數,返回索引位 1 上的元素。
示例4 如果把多個參數傳給 itemgetter,它構建的函數會返回提取的值構成的元組:
from operator import itemgetter
metro_data = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
cc_name = itemgetter(1, 0, 1, 2)
for city in metro_data:print(cc_name(city))''' ('JP', 'Tokyo', 'JP', 36.933)('IN', 'Delhi NCR', 'IN', 21.935)('MX', 'Mexico City', 'MX', 20.142)('US', 'New York-Newark', 'US', 20.104)('BR', 'Sao Paulo', 'BR', 19.649)'''itemgetter 使用 [] 運算符,因此它不僅支持序列,還支持映射和任何實現__getitem__ 方法的類。
3.operator.attrgetter
attrgetter 與 itemgetter 作用類似,它創建的函數根據名稱提取對象的屬性。
如果把多個屬性名傳給 attrgetter,它也會返回提取的值構成的元組。此外,如果參數名中包
含 .(點號),attrgetter 會深入嵌套對象,獲取指定的屬性。
from collections import namedtuple
from operator import attrgetter
metro_data = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
LatLong = namedtuple('LatLong', 'lat long')
Metropolis = namedtuple('Metropolis', 'name cc pop coord')
metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long))for name, cc, pop, (lat, long) in metro_data]
print(metro_areas[0])
print(metro_areas[0].coord.lat)
name_lat = attrgetter('name', 'coord.lat')
for city in sorted(metro_areas, key=attrgetter('coord.lat')):print(name_lat(city))# 輸出
Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.689722, long=139.691667))
35.689722
('Sao Paulo', -23.547778)
('Mexico City', 19.433333)
('Delhi NCR', 28.613889)
('Tokyo', 35.689722)
('New York-Newark', 40.808611)
下面是 operator 模塊中定義的部分函數(省略了以 _ 開頭的名稱,因為它們基本上是實
現細節)
import operator
te = [name for name in dir(operator) if not name.startswith('_')]
print(te)
#['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand', 'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul', 'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint', 'lshift', 'lt', 'matmul', 'methodcaller', 'mod', 'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub', 'truediv', 'truth', 'xor']
以 i 開頭、后面是另一個運算符的那些名稱(如iadd、iand 等),對應的是增量賦值運算符(如 +=、&= 等)。如果第一個參數是可變的,那么這些運算符函數會就地修改它;否則,作用與不帶 i 的函數一樣,直接返回運算結果。
在 operator 模塊余下的函數中,我們最后介紹一下 methodcaller。它的作用與attrgetter 和 itemgetter 類似,它會自行創建函數。methodcaller 創建的函數會在對象上調用參數指定的方法,如下例子:
示例 5methodcaller 使用示例:第二個測試展示綁定額外參數的方式:
from operator import methodcaller
s = 'The time has come'
upcase = methodcaller('upper')
print(upcase(s))
hiphnate = methodcaller('replace', ' ', '-')
print(hiphnate(s))
#輸出
THE TIME HAS COME
The-time-has-come
functools 模塊提供了一系列高階函數,其中最為人熟知的或許是 reduce,
我們在 5.2.1節已經介紹過。余下的函數中,最有用的是 partial 及其變體,partialmethod。
functools.partial 這個高階函數用于部分應用一個函數。**部分應用是指,基于一個函數創建一個新的可調用對象,把原函數的某些參數固定。**使用這個函數可以把接受一個或多個參數的函數改編成需要回調的 API,這樣參數更少。
示例 5-26 做了簡單的演示。
from operator import mul
from functools import partial
triple = partial(mul, 3)
print(triple(7))
print(list(map(triple, range(1, 10))))
#21
#[3, 6, 9, 12, 15, 18, 21, 24, 27]
示例 5-28 在示例 5-10 中定義的 tag 函數上使用 partial,凍結一個定位參數和一個關鍵
字參數。
示例 5-28 把 partial 應用到示例 5-10 中定義的 tag 函數上:
from functools import partial
def tag(name, *content, cls=None, **attrs):"""生成一個或多個HTML標簽"""if cls is not None:attrs['class'] = clsif 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)print(tag)
picture = partial(tag, 'img', cls='pic-frame')
print(picture(src='wumpus.jpeg'))
print(picture)
print(picture.func)
print(picture.args)
print(picture.keywords)