欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python operator模块

程序员文章站 2022-04-09 09:14:30
...

在函数式编程中,经常需要把算术运算符当作函数使用。例如,不使用递归计算阶乘。求 和可以使用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')