functools 模块¶
参考:
- functools — Higher-order functions and operations on callable objects
- Python functools 模块
- Python 模块简介 -- functools
functools 是 Python 函数式编程一个重要的内置库,但是内容实在不多。
主要的模块就几个
- cmp_to_key
- partial partialmethod
- reduce
- total_ordering
- update_wrapper
- wraps
cmp_to_key¶
在 list.sort 和 内建函数 sorted 中都有一个 key 参数,这个参数用来指定取元素的什么值进行比较,例如按字符串元素的长度进行比较
>>> x = ['hello','abc','iplaypython.com']
>>> x.sort(key=len)
>>> x
['abc', 'hello', 'iplaypython.com']
也就是说排序时会先对每个元素调用 key 所指定的函数,然后再排序。同时,sorted 和 list.sort 还提供了 cmp 参数来指定如何比较两个元素,但是在 Python 3 中该参数被去掉了。cmp_to_key 函数就是用来将老式的比较函数转化为 key 函数。用到 key 参数的函数还有 sorted(), min(), max(), heapq.nlargest(), itertools.groupby() 等
partial¶
本质上是一个装饰器,返回一个新的 partial 对象,当被调用时就和 func 一样,只不过通常参数会少于原 func 也就是提前给定原函数的一部分参数,再把它封装成另一个函数
原理大致如下
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
例子
>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
partialmethod¶
Return a new partialmethod descriptor which behaves like partial except that it is designed to be used as a method definition rather than being directly callable.
一般用于方法定义,而不是直接调用
>>> class Cell(object):
... def __init__(self):
... self._alive = False
... @property
... def alive(self):
... return self._alive
... def set_state(self, state):
... self._alive = bool(state)
... set_alive = partialmethod(set_state, True)
... set_dead = partialmethod(set_state, False)
...
>>> c = Cell()
>>> c.alive
False
>>> c.set_alive()
>>> c.alive
True
reduce¶
在 Python2 中等同于内建函数 reduce,但是在 Python3 中内建的 reduce 函数被移除,只能使用 functools.reduce
简单的说就是 reduce the sequence to a single value, function 必须是接受两个参数的,对一个序列一直执行,直直到最后变为一个值
其执行原理大概如下
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
value = next(it)
else:
value = initializer
for element in it:
value = function(value, element)
return value
例子,注意 reduce 还有一个默认参数 initializer
from functools import reduce
>>> function = lambda x, y: x+y
>>> iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> reduce(function, iterable)
45
>>> reduce(function, iterable, 10)
55
total_ordering¶
这是一个类装饰器,给定一个类,这个类定义了一个或多个比较排序方法,这个类装饰器将会补充其余的比较方法,减少了自己定义所有比较方法时的工作量。
被修饰的类必须至少定义 lt(), le(), gt() 或 ge() 中的一个,同时,被修饰的类还应该提供 eq() 方法。
@total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
update_wrapper¶
用 partial 包装的函数是没有 name 和 doc,这使得依赖于这些属性的代码可能无法正常工作。update_wrapper 可以拷贝被包装函数的 name、module、doc 和 dict 属性到新的封装函数中去,其实现也非常简单
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
return wrapper
update_wrapper 主要用在装饰器函数中,以确保被装饰的保留原理的属性,调用 update_wrapper 需要至少两个参数,用起来不是很方便这可能是 wraps 封装出现的原因之一
def wrap(func):
def call_it(*args, **kwargs):
print "calling", func.__name__
return func(*args, **kwargs)
return call_it
@wrap
def hello():
print "hello"
from functools import update_wrapper
def wrap2(func):
def call_it(*args, **kwargs):
print "calling", func.__name__
return func(*args, **kwargs)
return update_wrapper(call_it, func)
@wrap2
def hello2():
print "hello2"
>>> print hello.__name__
call_it
>>> print hello2.__name__
hello2
wraps¶
wraps 函数是为了在装饰器中方便的拷贝被装饰函数的签名,而对 update_wrapper 做的一个包装,其实现如下:
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
示例
from functools import wraps
def wrap3(func):
@wraps(func)
def call_it(*args, **kwargs):
print "calling", func.__name__
return func(*args, **kwargs)
return call_it
@wrap3
def hello3(func):
print "hello3"
print hello3.__name__ # hello3