[Python标准库]functools——管理函数的工具
作用:处理其他函数的函数。
Python 版本:2.5 及以后版本
functools 模块提供了一些工具来调整或扩展函数和其他可回调对象,而不必完全重写。
修饰符
functools 模块提供的主要工具是 partial 类,它可以用来“包装”一个有默认参数的可回调对象。得到的对象本身是可回调的,可以看作就像是原来的函数。它与原函数的参数完全相同,调用时也可以提供额外的位置或命名参数。可以使用 partial 而不是 lambda 为函数提供默认参数,有些参数可以不指定。
1. partial 对象
下面这个例子显示了函数 myfunc() 的两个简单的 partial 对象。show_details() 的输出包含这个部分对象的 func、args 和 keywords 属性。
作用:处理其他函数的函数。
Python 版本:2.5 及以后版本
functools 模块提供了一些工具来调整或扩展函数和其他可回调对象,而不必完全重写。
修饰符
functools 模块提供的主要工具是 partial 类,它可以用来“包装”一个有默认参数的可回调对象。得到的对象本身是可回调的,可以看作就像是原来的函数。它与原函数的参数完全相同,调用时也可以提供额外的位置或命名参数。可以使用 partial 而不是 lambda 为函数提供默认参数,有些参数可以不指定。
1. partial 对象
下面这个例子显示了函数 myfunc() 的两个简单的 partial 对象。show_details() 的输出包含这个部分对象的 func、args 和 keywords 属性。
import functools
def myfunc(a, b=2):
"""Docstring for myfunc()."""
print ' called myfunc with:', (a, b)
return
def show_details(name, f, is_partial=False):
"""Show details of a callable object."""
print '%s:' % name
print ' object:', f
if not is_partial:
print ' __name__:', f.__name__
if is_partial:
print ' func:', f.func
print ' args:', f.args
print ' keywords:', f.keywords
return
show_details('myfunc', myfunc)
myfunc('a', 3)
print
# Set a different default value for 'b', but require
# the caller to provide 'a'.
p1 = functools.partial(myfunc, b=4)
show_details('partial with named default', p1, True)
p1('passing a')
p1('override b', b=5)
print
# Set default value for both 'a' and 'b'.
p2 = functools.partial(myfunc, 'default a', b=99)
show_details('partial with named default', p2, True)
p2()
p2(b='override b')
print
print 'Insufficient arguments:'
p1()
在这个例子的最后,调用了之前创建的第一个 partial,但没有为 a 传入一个值,这就导致一个异常。
2. 获取函数属性
默认情况下,partial 对象没有 __name__ 或 __doc__ 属性,如果没有这些属性,修饰的函数将更难调试。使用 update_wrapper() 可以从原函数将属性复制或添加到 partial 对象。
2. 获取函数属性
默认情况下,partial 对象没有 __name__ 或 __doc__ 属性,如果没有这些属性,修饰的函数将更难调试。使用 update_wrapper() 可以从原函数将属性复制或添加到 partial 对象。
import functools
def myfunc(a, b=2):
"""Docstring for myfunc()."""
print ' called myfunc with:', (a, b)
return
def show_details(name, f):
"""Show details of a callable object."""
print '%s:' % name
print ' object:', f
print ' __name__:',
try:
print f.__name__
except AttributeError:
print '(no __name__)'
print ' __doc__:', repr(f.__doc__)
print
return
show_details('myfunc', myfunc)
p1 = functools.partial(myfunc, b=4)
show_details('raw wrapper', p1)
print 'Updating wrapper:'
print ' assign:', functools.WRAPPER_ASSIGNMENTS
print ' update:', functools.WRAPPER_UPDATES
functools.update_wrapper(p1, myfunc)
show_details('updated wrapper', p1)
添加到包装器的属性在 WRAPPER_ASSIGNMENTS 中定义,而 WRAPPER_UPDATES 列出了要修改的值。
3. 其他可回调对象
Partial 适用于任何可回调对象,而不只是单独的函数。
3. 其他可回调对象
Partial 适用于任何可回调对象,而不只是单独的函数。
import functools
class MyClass(object):
"""Demonstration class for functools"""
def method1(self, a, b=2):
"""Docstring for method1()."""
print ' called method1 with:', (self, a, b)
return
def method2(self, c, d=5):
"""Docstring for method2"""
print ' called method2 with:', (self, c, d)
return
wrapped_method2 = functools.partial(method2, 'wrapped c')
functools.update_wrapper(wrapped_method2, method2)
def __call__(self, e, f=6):
"""Docstring for MyClass.__call__"""
print ' called object with:', (self, e, f)
return
def show_details(name, f):
"""Show details of a callable object."""
print '%s:' % name
print ' object:', f
print ' __name__:',
try:
print f.__name__
except AttributeError:
print '(no __name__)'
print ' __doc__:', repr(f.__doc__)
return
o = MyClass()
show_details('method1 straight', o.method1)
o.method1('no default for a', b=3)
print
p1 = functools.partial(o.method1, b=4)
functools.update_wrapper(p1, o.method1)
show_details('method1 wrapper', p1)
print
show_details('method2', o.method2)
o.method2('no default for c', d=6)
print
show_details('wrapped method2', o.wrapped_method2)
o.wrapped_method2('no default for c', d=6)
print
show_details('instance', o)
o('no default for e')
print
p2 = functools.partial(o, f=7)
show_details('instance wrapper', p2)
p2('e goes here')
这个例子由一个实例以及实例的方法来创建部分对象。
4. 为修饰符获取函数属性
在修饰符中使用时,更新包装的可回调对象的属性尤其有用,因为变换后的函数最后会得到原“裸”函数的属性。
4. 为修饰符获取函数属性
在修饰符中使用时,更新包装的可回调对象的属性尤其有用,因为变换后的函数最后会得到原“裸”函数的属性。
import functools
def show_details(name, f):
"""Show details of a callable object."""
print '%s:' % name
print ' object:', f
print ' __name__:',
try:
print f.__name__
except AttributeError:
print '(no __name__)'
print ' __doc__:', repr(f.__doc__)
print
return
def simple_decorator(f):
@functools.wraps(f)
def decorated(a='decorated defaults', b=1):
print ' decorated:', (a, b)
print ' ',
f(a, b=b)
return
return decorated
def myfunc(a, b=2):
"myfunc() is not complicated"
print ' myfunc:', (a, b)
return
# The raw function
show_details('myfunc', myfunc)
myfunc('unwrapped, default b')
myfunc('unwrapped, passing b', 3)
print
# Wrap explicitly
wrapped_myfunc = simple_decorator(myfunc)
show_details('wrapped_myfunc', wrapped_myfunc)
wrapped_myfunc()
wrapped_myfunc('args to wrapped', 4)
print
# Wrap with decorator syntax
@simple_decorator
def decorated_myfunc(a, b):
myfunc(a, b)
return
show_details('decorated_myfunc', decorated_myfunc)
decorated_myfunc()
decorated_myfunc('args to decorated', 4)
functools 提供了一个修饰符 wraps(),它会对所修饰的函数应用 update_wrapper()。
比较
在 Python 2 中,类可以定义一个 __cmp__() 方法,后者会根据这个对象小于、等于还是大于所比较的元素返回 -1、0 或 1。Python 2.1 引入了富比较(rich comparison)方法 API(__lt__()、__le__()、__eq__()、__ne__()、__gt__() 和 __ge__()),可以完成一个比较操作并返回一个 Boolean 值。Python 3 则废弃了 __cmp__() 而代之以这些新方法,所以 functools 提供了一些工具,以便于编写 Python 2 类而且同时符合 Python 3 中新的比较需求。
1. 富比较
设计富比较 API 是为了支持涉及复杂比较的类,从而以最高效的方式实现各个测试。不过,对于比较相对简单的类,手动地创建各个富比较方法就没有必要了。total_ordering() 类修饰符取一个提供了部分方法的类,并添加其余的方法。
比较
在 Python 2 中,类可以定义一个 __cmp__() 方法,后者会根据这个对象小于、等于还是大于所比较的元素返回 -1、0 或 1。Python 2.1 引入了富比较(rich comparison)方法 API(__lt__()、__le__()、__eq__()、__ne__()、__gt__() 和 __ge__()),可以完成一个比较操作并返回一个 Boolean 值。Python 3 则废弃了 __cmp__() 而代之以这些新方法,所以 functools 提供了一些工具,以便于编写 Python 2 类而且同时符合 Python 3 中新的比较需求。
1. 富比较
设计富比较 API 是为了支持涉及复杂比较的类,从而以最高效的方式实现各个测试。不过,对于比较相对简单的类,手动地创建各个富比较方法就没有必要了。total_ordering() 类修饰符取一个提供了部分方法的类,并添加其余的方法。
import functools
import inspect
from pprint import pprint
@functools.total_ordering
class MyObject(object):
def __init__(self, val):
self.val = val
def __eq__(self, other):
print ' testing __eq__(%s, %s)' % (self.val, other.val)
return self.val == other.val
def __gt__(self, other):
print ' testing __gt__(%s, %s)' % (self.val, other.val)
return self.val > other.val
print 'Methods:\n'
pprint(inspect.getmembers(MyObject, inspect.ismethod))
a = MyObject(1)
b = MyObject(2)
print '\nComparisons:'
for expr in [ 'a < b', 'a <= b', 'a == b', 'a >= b', 'a > b' ]:
print '\n%-6s:' % expr
result = eval(expr)
print ' result of %s: %s' % (expr, result)
这个类必须提供 __eq__() 和另外一个富比较方法的实现。修饰符会增加其余方法的实现,它们要利用类提供的比较来完成工作。
2. 比对序
由于 Python 3 中废弃了老式比较函数,因此 sort() 之类的函数中也不再支持 cmp 参数。对于使用了比较函数的 Python 2 程序,可以用 cmp_to_key() 将它们转换为一个返回比对键(collation key)的函数,这个键用于确定元素在最终序列中的位置。
2. 比对序
由于 Python 3 中废弃了老式比较函数,因此 sort() 之类的函数中也不再支持 cmp 参数。对于使用了比较函数的 Python 2 程序,可以用 cmp_to_key() 将它们转换为一个返回比对键(collation key)的函数,这个键用于确定元素在最终序列中的位置。
import functools
class MyObject(object):
def __init__(self, val):
self.val = val
def __str__(self):
return 'MyObject(%s)' % self.val
def compare_obj(a, b):
"""Old-style comparison function."""
print 'comparing %s and %s' % (a, b)
return cmp(a.val, b.val)
# Make a key function using cmp_to_key()
get_key = functools.cmp_to_key(compare_obj)
def get_key_wrapper(o):
"""Wrapper function for get_key to allow for print statements."""
new_key = get_key(o)
print 'key_wrapper(%s) -> %s' % (o, new_key)
return new_key
objs = [ MyObject(x) for x in xrange(5, 0, -1) ]
for o in sorted(objs, key=get_key_wrapper):
print o
正常情况下,可以直接使用 cmp_to_key(),不过这个例子中引入了一个额外的包装器函数,从而在调用 key 函数时可以输出更多的信息。
如输出所示,sorted() 首先对序列中的每一个元素调用 get_key_wrapper() 来生成一个键。cmp_to_key() 返回的键是 functools 中定义的一个类的实例,这个类使用所传入的老式比较函数来实现富比较 API。所有键都创建之后,将通过比较这些键对序列排序。
如输出所示,sorted() 首先对序列中的每一个元素调用 get_key_wrapper() 来生成一个键。cmp_to_key() 返回的键是 functools 中定义的一个类的实例,这个类使用所传入的老式比较函数来实现富比较 API。所有键都创建之后,将通过比较这些键对序列排序。