python operator模块

在函数式编程中,经常需要把算术运算符当作函数使用。例如,不使用递归计算阶乘。求 和可以使用sum 函数,但是求积则没有这样的函数。我们可以使用reduce 函数,但是需要一个函数计算序列中两个元素之积。如下展示如何使用 lambda 表达式解决这个问题。

from functools import reduce 


def fact(n): 
	return reduce(lambda a, b: a*b, range(1, n+1))

operator 模块为多个算术运算符提供了对应的函数,从而避免编写 lambda a, b: a*b 这种 平凡的匿名函数。使用算术运算符函数,可以把示例进行改写。

from functools import reduce 
from operator import mul 


def fact(n):
	return reduce(mul, range(1, n+1))


operator 模块中还有一类函数,能替代从序列中取出元素或读取对象属性的 lambda 表达 式:因此,itemgetterattrgetter 其实会自行构建函数。

如下示例 展示了 itemgetter 的常见用途:根据元组的某个字段给元组列表排序。在这个示 例中,按照国家代码(第 2 个字段)的顺序打印各个城市的信息。其实,itemgetter(1) 的 作用与 lambda fields: fields[1] 一样:创建一个接受集合的函数,返回索引位 1 上的元 素。

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,它构建的函数会返回提取的值构成的元组:

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) 

for city in metro_data: 
	print(cc_name(city)) 

itemgetter 使用 [] 运算符,因此它不仅支持序列,还支持映射和任何实现 __getitem__ 方 法的类。

attrgetteritemgetter 作用类似,它创建的函数根据名称提取对象的属性。如果把 多个属性名传给 attrgetter,它也会返回提取的值构成的元组。此外,如果参数名中包 含 .(点号),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)) 

下面是 operator 模块中定义的部分函数(省略了以 _ 开头的名称,因为它们基本上是实现 细节)

 s = [name for name in dir(operator) if not name.startswith('_')] 
 print(a)
 
 # 结果:
['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand', 'iconcat', 'ifloordiv', 'ilshift', 'imod', 'imul', 'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint', 'lshift', 'lt', 'methodcaller', 'mod', 'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub', 'truediv', 'truth', 'xor']

这 52 个名称中大部分的作用不言而喻。以 i 开头、后面是另一个运算符的那些名称(如 iaddiand 等),对应的是增量赋值运算符(如 +=、&= 等)。如果第一个参数是可变的,那 么这些运算符函数会就地修改它;否则,作用与不带 i 的函数一样,直接返回运算结果。

operator 模块余下的函数中,我们最后介绍一下 methodcaller。它的作用与 attrgetteritemgetter 类似,它会自行创建函数。methodcaller 创建的函数会在对象上调用参数指 定的方法,如下示例 :

"""
operator 中 methodcaller
它的作用与 attrgetter 和 itemgetter 类似,它会自行创建函数。
methodcaller 创建的函数会在对象上调用参数指 定的方法,
"""
from operator import methodcaller


s = 'The time has come'
upcase = methodcaller('upper')
print(upcase(s))

hiphenate = methodcaller('replace', ' ', '-')
print(hiphenate(s))

示例中的第一个测试只是为了展示 methodcaller 的用法,如果想把 str.upper 作为函 数使用,只需在 str 类上调用,并传入一个字符串参数,如下所示:

print(str.upper(s)) 

示例中的第二个测试表明,methodcaller 还可以冻结某些参数,也就是部分应用 (partial application),这与 functools.partial 函数的作用类似。

functools 模块提供了一系列高阶函数,其中最为人熟知的或许是 reduce,余下的函数中,最有用的是 partial 及其变体,partialmethod

functools.partial 这个高阶函数用于部分应用一个函数。部分应用是指,基于一个函数创 建一个新的可调用对象,把原函数的某些参数固定。使用这个函数可以把接受一个或多个 参数的函数改编成需要回调的 API,这样参数更少。下面示例做了简单的演示。

使用 partial 把一个两参数函数改编成需要单参数的可调用对象

from operator import mul 
from functools import partial 


# 使用 mul 创建 triple 函数,把第一个定位参数定为 3。 
triple = partial(mul, 3) 
print(triple(7))
# 21

print(list(map(triple, range(1, 10)))) 
# [3, 6, 9, 12, 15, 18, 21, 24, 27]

如果处理 多国语言编写的文本,在比较或排序之前可能会想使用 unicode.normalize('NFC', s) 处理 所有字符串 s。如果经常这么做,可以定义一个 nfc 函数,如示例 所示。

使用 partial 构建一个便利的 Unicode 规范化函数

import unicodedata, functools 


nfc = functools.partial(unicodedata.normalize, 'NFC') 
s1 = 'café' 
s2 = 'cafe\u0301' 
print(s1, s2)
# ('café', 'café') 

s1 == s2 
# False 
nfc(s1) == nfc(s2) 
# True

partial 的第一个参数是一个可调用对象,后面跟着任意个要绑定的定位参数关键字参数
使用 partial,冻结一个定位参数和一个关键字 参数。

from functools import partial 


def tag(name, *content, cls=None, **attrs): 
	"""生成一个或多个HTML标签""" 
	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)


# 冻结
picture = partial(tag, 'img', cls='pic-frame') 

猜你喜欢

转载自blog.csdn.net/MZP_man/article/details/103658227