Python operator模块和functools模块

Python 中的 operator 模块和 functools 模块主要用于函数式编程

operator 模块

1)算术运算符函数

函数式编程中,经常需要把算术运算符当作函数使用,例如求阶乘

使用 lambda 的例子

from functools import reduce

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

使用 operator 模块的例子
from functools import reduce
from operator import mul

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

operator 模块位多个运算符提供了对应的函数,从而避免了写 lambda 表达式,形式上更简洁,类似的还有 add、sub 等函数


 
2)itemgetter

itemgetter 函数用于从序列中(根据索引)或者映射类型中(根据键)读取元素

>>> from operator import itemgetter
>>>
>>>
>>> a = ['h', 'e', 'l', 'l', 'o']
>>> b = (2, 3, 4)
>>> c = range(5)
>>> f = itemgetter(1)  # 区索引为 1 位置的值
>>> f(a)
'e'
>>> f(b)
3
>>> f(c)
1

>>> d = {'name': 'hhh', 'age': 18}
>>> f = itemgetter('name')  # 取键为 name 的元素
>>> f(d)
'hhh'

itemgetter 返回的是一个可调用对象,其参数是序列或者映射类型


itemgetter 多用于作为高阶函数的参数

>>> users = [{'name': '犬夜叉', 'age': 100}, {'name': '戈薇', 'age': 14}, {'name': '珊瑚', 'age': 20},{'name': '弥勒', 'age': 22}]
>>> users.sort(key=itemgetter('age'))  # 按年龄排序
>>> print(users)
[{'name': '戈薇', 'age': 14}, {'name': '珊瑚', 'age': 20}, {'name': '弥勒', 'age': 22}, {'name': '犬夜叉', 'age': 100}]

事实上,itemgetter 不仅支持序列和映射类型,还支持任何实现了 __getitem__ 方法的类

如果 itemgetter 传入多个参数,返回的结果会是一个元组

>>> a = [1, 2, 3, 4, 5]
>>> f = itemgetter(0, -1)
>>> f(a)
(1, 5)


 
3)attrgetter

attrgetter 与 itemgetter 作用类似,它根据名称提取对象的属性

>>> from collections import namedtuple
>>> from operator import attrgetter
>>> profile = namedtuple('Profile', ['name', 'age', 'hobby'])
>>>
>>> profile
<class '__main__.Profile'>

>>> quanyecha = profile(name='犬夜叉', age=100, hobby='坐下')
>>> f = attrgetter('name', 'age')
>>>
>>> f(quanyecha)
('犬夜叉', 100)

可以看到 attrgetter 跟 itemgetter 几乎一样,也可以传入多个参数获取元组

attragettter 还可以传入带 . 的参数,attrgetter 会深入嵌套对象,获取指定的属性

>>> profile = namedtuple('Profile', ['name', 'age', 'spouse'])
>>>
>>> gewei = profile(name='戈薇', age=14, spouse='犬夜叉')
>>> quanyecha = profile(name='犬夜叉', age=100, spouse=gewei)
>>>
>>> get_spouse_name = attrgetter('spouse.name')
>>> get_spouse_age = attrgetter('spouse.age')
>>>
>>> get_spouse_name(quanyecha)
'戈薇'
>>> get_spouse_age(quanyecha)
14


 

functools 模块

functools 模块提供了一系列高阶函数,最常见的是 reduce ,除了 reduce ,还有 partial 函数比较有用

1)reduce

reduce 用于把某个操作连续运用到序列的元素上,累计之前的结果,把一系列归约成一个值

>>> from functools import reduce
>>> from operator import add
>>> reduce(add, range(100))
4950


 
2)partial

partial 用于部分应用一个函数,部分应用指基于一个函数创建一个新的可调用对象,把原函数的某些参数固定

先来看一个简单的例子

>>> from functools import partial
>>> from operator import mul
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(6)
18
>>> list(map(triple, range(1,10)))
[3, 6, 9, 12, 15, 18, 21, 24, 27]

>>> triple.func
<built-in function mul>
>>>
>>> triple.args
(3,)

其中 mul 函数接收两个参数,partial(mul, 3) 返回一个可调用对象,把 mul 函数的第一个参数固定为 3,这样之后再调用这个可调用对象就只需要传入一个参数

下面来看一个笔画排序的例子

class Stroke:
    """汉字笔画排序"""
    @classmethod
    def get_stroke_order(cls, chars: str):
        """获取排序"""
        return tuple(cls.stroke.get(i, math.inf) for i in chars)

    @classmethod
    def compare(cls, a: tuple, b: tuple):
        for a_i, b_i in itertools.zip_longest(a, b, fillvalue=0):
            if a_i < b_i:
                return -1
            elif a_i > b_i:
                return 1
            else:
                pass
        return 0

    @classmethod
    def compare_by_stroke(cls, x, y, key: Callable):
        a = key(x)
        b = key(y)
        return cls.compare(a, b)

    @classmethod
    def sort_by_stroke(cls, obj: Iterable, key: Callable):
        cmp = functools.partial(cls.compare_by_stroke, key=key)
        return sorted(obj, key=functools.cmp_to_key(cmp))

Stroke 的使用方式为

>>> pprint(p)
[{'age': 19, 'name': '陈世美', 'score': 29},
{'age': 18, 'name': '张大千', 'score': 27},
{'age': 29, 'name': '林强', 'score': 34},
{'age': 56, 'name': '王大志', 'score': 21},
{'age': 34, 'name': '王大', 'score': 32},
{'age': 67, 'name': '王大福', 'score': 13}]
>>>
>>> for i in p:
...     i['order'] = Stroke.get_stroke_order(i['name'])

>>> q = Stroke.sort_by_stroke(p, lambda x: x['order'])

上面的例子里把 cls.compare_by_stroke 函数的 key 参数固定住,返回一个可调用对象,再通过可调用对象作为 sorted 函数的 key 来排序


个人认为 partial 函数的作用在于定制化,先写一个通用的函数,然后根据实际需要固定这个函数里的某些参数得到新的可调用对象

补充说一下,上面的 cmp_to_key 是一种兼容的做法,Python3 不支持比较函数,再一些接受 key 的函数中(例如 sorted,sort,min,max),key 仅仅支持一个参数,就无法实现两个参数之间的比较,采用 cmp_to_key,可以接受两个参数,将两个参数做处理,转换成一个参数,就可以应用于 key 关键字之后

>>> from functools import cmp_to_key
>>> l = ['2', '3', '1', '10', 8]
>>> sorted(l, key=cmp_to_key(lambda x,y:int(x)-int(y)))
['1', '2', '3', 8, '10']
原文地址:https://www.cnblogs.com/zzliu/p/14223340.html