functools------管理函数的工具
作用:处理其他函数的函数。
functools模块提供了一些工具来调整或扩展函数和其他可回调对象,而不必完全重写。
1.修饰符
functools模块提供的主要工具是partial类,它可以用来“包装”一个有默认参数的可回调对象。得到的对象本身是可回调的,可以看作就像是原来的函数。它与原函数的参数完全回调,调用时也可以提供额外的位置或命名参数。可以使用partial而不是lambda为函数,有些参数可以不指定。
partial对象
下面这个例子显示了函数myfunc()的两个简单的partial对象。show_details()的输出包含这个部分对象的func,args和keywords的属性。
import functools
def myfunc(a, b=2):
print('called myfunc with:', (a, b))
return
def show_details(name, f, is_partial=False):
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 balues for both 'a' and 'b'
p2 = functools.partial(myfunc, 'default a', b=99)
show_details('partial with defaults', p2, True)
p2()
p2(b = 'override b ')
print('********************************************************')
print('Insufficient arguments:')
获取函数属性
默认情况下,partial对象没有__name__或__doc__属性。如果没有这些属性,写实的函数将更难调试。使用updata_wrapper()可以从原函数将属性复制或添加到partial对象。
def myfunc(a, b=2):
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:
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列出了要修改的值。
myfunc:
object: <function myfunc at 0x00000000022E9B88>
__name__
myfunc
__doc__ None
**********************************
raw wrapper:
object: functools.partial(<function myfunc at 0x00000000022E9B88>, b=4)
__name__
(no __name__)
__doc__ 'partial(func, *args, **keywords) - new function with partial application\n of the given arguments and keywords.\n'
**********************************
updating wrapper:
assign: ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
update: ('__dict__',)
updated wrapper:
object: functools.partial(<function myfunc at 0x00000000022E9B88>, b=4)
__name__
myfunc
__doc__ None
**********************************
其他可回调对象
Partial适用于任何可回调对象,而不只是单独的函数。
class MyClass():
''' Demonstration class for functools'''
def method1(self,a,b=2):
'''Docstring for method1()'''
print('called method1 with:',(self,a,b))
def method2(self,c,d=5):
'''Docstirng for method2'''
print('called method2 with:',(self,c,d))
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))
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__))
o = MyClass()
show_details('method1 straight', o.method1)
o.method1('no default for a', b=3)
print('*********************11111111***********************')
p1 = functools.partial(o.method1,b=4)
functools.update_wrapper(p1,o.method1)
show_details('method1 wrapper',p1)
p1('a goes here')
print('*********************22222222*********************')
show_details('method2', o.method2)
o.method2('no default for c',d=6)
print('*********************33333333*********************')
show_details('wrapped method2', o.wrapped_method2)
o.wrapped_method2('no default for c',d=6)
print('*********************44444444*********************')
这个例子由一个实例以及实例的方法来创建部分对象。
method1 straight:
object: <bound method MyClass.method1 of <__main__.MyClass object at 0x0000000003E199C8>>
__main__
method1
__doc__ 'Docstring for method1()'
called method1 with: (<__main__.MyClass object at 0x0000000003E199C8>, 'no default for a', 3)
*********************11111111***********************
method1 wrapper:
object: functools.partial(<bound method MyClass.method1 of <__main__.MyClass object at 0x0000000003E199C8>>, b=4)
__main__
method1
__doc__ 'Docstring for method1()'
called method1 with: (<__main__.MyClass object at 0x0000000003E199C8>, 'a goes here', 4)
*********************22222222*********************
method2:
object: <bound method MyClass.method2 of <__main__.MyClass object at 0x0000000003E199C8>>
__main__
method2
__doc__ 'Docstirng for method2'
called method2 with: (<__main__.MyClass object at 0x0000000003E199C8>, 'no default for c', 6)
*********************33333333*********************
wrapped method2:
object: functools.partial(<function MyClass.method2 at 0x0000000002556318>, 'wrapped c')
__main__
method2
__doc__ 'Docstirng for method2'
called method2 with: ('wrapped c', 'no default for c', 6)
*********************44444444*********************
为修饰符获取函数属性
在修饰符中使用时,更新包赚的可回调对象的属性尤其有用,因为变换后的函数最后会得到原‘裸’函数的属性。
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):
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()。
myfunc:
object: <function myfunc at 0x00000000025CB438>
__name__:
myfunc
__doc__ None
****************************
myfunc: ('unwrapped,default b', 2)
myfunc: ('unwrapped,passing b', 3)
*****************************
wrapped_myfunc:
object: <function myfunc at 0x00000000025CBC18>
__name__:
myfunc
__doc__ None
****************************
decorated: ('decorated defaults', 1)
******************************
myfunc: ('decorated defaults', 1)
decorated: ('args to wrapped', 4)
******************************
myfunc: ('args to wrapped', 4)
******************************
decorated_myfunc:
object: <function decorated_myfunc at 0x00000000123FD0D8>
__name__:
decorated_myfunc
__doc__ None
****************************
decorated: ('decorated defaults', 1)
******************************
myfunc: ('decorated defaults', 1)
decorated: ('args to decorated', 4)
******************************
myfunc: ('args to decorated', 4)
2.比较
在python2中,类可以定义一个__cmp__()方法,后者会根据这个对象小于,等于还是大于所比较的元素返回-1,0或1.python2.1引入了富比较(rich comparsion)方法API(__lt__(),__le__(),__eq__(),__ne__(),__gt__()和__ge__()),可以完成一个比较操作并返回一个Boolean值。python3则废弃了__cmp__()而代之以这些新方法。所以functools提供了一些工具,以便于编写python2类而且同时符合python3中新的比较需求。
富比较
设计富比较API是为了支持涉及复杂比较的类,从而以最高效的方式实现各个测试。不过,对于比较相对简单的类,手动地创建各个富比较方法就没有必要了。total_ordering()类修饰符取一个提供了部分方法的类,并添加其余的方法。
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__()和另一个富比较方法的实现。修饰符会增加其余方法的实现,它们利用类提供的比较来完成工作。
Methods: [] Comparisons: a < b : testing __gt__(1, 2) testing __eq__(1, 2) result of a < b: True a <= b: testing __gt__(1, 2) result of a <= b: True a == b: testing __eq__(1, 2) result of a == b: False a >=b : testing __gt__(1, 2) testing __eq__(1, 2) result of a >=b: False a > b : testing __gt__(1, 2) result of a > b: False
注:在python中,eval()方法是一个经常用到的函数,我们在编写输入函数的时候,需要把input()函数写进eval()方法中,这样得到的输入结果就不会是字符串类型的了。
比对序
由于python3中废弃了老式比较函数,因此sort()之类的函数中也不再支持cmp参数。对于使用了比较函数的python2程序,可以用cmp_to_key()将它们转换为一个返回比对键(collation key)的函数,这个键用于确定元素在最终序列中的位置。

浙公网安备 33010602011771号