文章目录
- 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)