functools模块的作用
functools模块提供了一些工具来调整或扩展函数和其他callable对象,从而不必完全重写。
1、functools.partial,给函数默认传参,再使用函数一样的调用方式的示例
importfunctoolsdef myfunc(a, b=2):'''定义myfunc函数,打印变量值'''
print('called myfunc with:', (a, b))def show_details(name, f, is_partial=False):"传入函数名和函数的对象,判断是否使用functools工具,进行参数的打印调用"
print('{}:'.format(name))print('object:', f)if notis_partial:print('__name__:', f.__name__)ifis_partial:print('func:', f.func)print('args:', f.args)print('keywords:', f.keywords)returnshow_details('myfunc', myfunc)
myfunc('a', 3)#运行结果#myfunc:#object: #__name__: myfunc#called myfunc with: ('a', 3)
print()
p1= functools.partial(myfunc, b=4)
show_details('partial with named default', p1, True)#运行结果#partial with named default:#object: functools.partial(, b=4)#func: #args: ()#keywords: {'b': 4}
#直接使用functools修饰一个函数,让调用变得更加简单
p1('passing a')
p1('override b', b=5)print()
p2= functools.partial(myfunc, 'default a', b=99)
show_details('partial with defaults', p2, True)#运行结果#partial with defaults:#object: functools.partial(, 'default a', b=99)#func: #args: ('default a',)#keywords: {'b': 99}
p2()#called myfunc with: ('default a', 99)
p2(b='override b')#called myfunc with: ('default a', 'override b')
print()print('参数缺少')
p1()#TypeError: myfunc() missing 1 required positional argument: 'a'
functools_partial.py
运行效果
TypeError: myfunc() missing 1 required positional argument: 'a'myfunc:
object:
__name__: myfunc
called myfunc with: ('a', 3)
partial with named default:
object: functools.partial(, b=4)
func:args: ()
keywords: {'b': 4}
called myfunc with: ('passing a', 4)
called myfunc with: ('override b', 5)
partial with defaults:
object: functools.partial(, 'default a', b=99)
func:args: ('default a',)
keywords: {'b': 99}
called myfunc with: ('default a', 99)
called myfunc with: ('default a', 'override b')
参数缺少
2、更新被functools.partial装饰过的的内置属性值
importfunctoolsdef myfunc(a, b=2):'''定义myfunc函数,打印变量值'''
print('called myfunc with:', (a, b))def show_details(name, f, is_partial=False):"传入函数名和函数的对象,判断是否使用functools工具,进行参数的打印调用"
print('{}:'.format(name))print('object:', f)print('__name__:', end=' ')try:print(f.__name__)exceptAttributeError:print('(no __name__)')print('__doc__', repr(f.__doc__))print()
show_details('myfunc', myfunc)#myfunc: 信息输出正常#object: #__name__: myfunc#__doc__ '\n 定义myfunc函数,打印变量值\n '
p1= functools.partial(myfunc, b=4)
show_details('raw wrapper', p1)#raw wrapper: 装饰过,函数名和#object: functools.partial(, b=4)#__name__: (no __name__)#__doc__ 'partial(func, *args, **keywords) - new function with partial application\n of the given arguments and keywords.\n'
print('Updating wrapper:')print('assign:', functools.WRAPPER_ASSIGNMENTS)print('update:', functools.WRAPPER_UPDATES)print()#Updating wrapper: 更新包装类的数据#assign: ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')#update: ('__dict__',)
#更新包装类后的数据
functools.update_wrapper(p1, myfunc)
show_details('updated wrapper', p1)#updated wrapper:#object: functools.partial(, b=4)#__name__: myfunc#__doc__ '\n 定义myfunc函数,打印变量值\n '
functools_update_wrapper.py
运行效果
myfunc:
object:
__name__: myfunc__doc__ '\n 定义myfunc函数,打印变量值\n'raw wrapper:
object: functools.partial(, 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(, b=4)__name__: myfunc__doc__ '\n 定义myfunc函数,打印变量值\n'
3、functools.partial装饰类,并且修改内置的属性
importfunctoolsclassMyClass:"模拟工具示范课"
def __call__(self, e, f=6):"Docstring for MyClass.__call__"
print('called object with:', (self, e, f))defshow_details(name, f):"Show details of a callable object."
print('{}:'.format(name))print('object:', f)print('__name__:', end=' ')try:print(f.__name__)exceptAttributeError:print('(no __name__)')print('__doc__', repr(f.__doc__))returno=MyClass()
show_details('instance', o)
o('e goes here')print()
p= functools.partial(o, e='default for e', f=8) #如果是装饰,类的话,默认实例后,会调用类的内置方法__call__
functools.update_wrapper(p, o)
show_details('instance wrapper', p)
p()
functools_callable.py
运行效果
instance:
object:<__main__.myclass object at>
__name__: (no __name__)__doc__ '模拟工具示范课'called object with: (<__main__.myclass object at>, 'e goes here', 6)
instance wrapper:
object: functools.partial(<__main__.myclass object at>, e='default for e', f=8)__name__: (no __name__)__doc__ '模拟工具示范课'called object with: (<__main__.myclass object at>, 'default for e', 8)
4、functools.partialmethod与functools.partial的区别
importfunctoolsdef standalone(self, a=1, b=2):"Standalone function"
print('called standalone with:', (self, a, b))if self is notNone:print('self.attr =', self.attr)classMyClass:"Demonstration class for functools"
def __init__(self):
self.attr= 'instance attribute'method1=functools.partialmethod(standalone)
method2=functools.partial(standalone)
o=MyClass()print('standalone')
standalone(None)print()print('method1 as partialmethod')
o.method1()print()print('method2 as partial')try:
o.method2()exceptTypeError as err:print('ERROR: {}'.format(err))
functools_partialmethod.py
运行效果
standalone
called standalone with: (None,1, 2)
method1 as partialmethod
called standalone with: (<__main__.myclass object at>, 1, 2)
self.attr=instance attribute
method2 as partial
ERROR: standalone() missing1 required positional argument: 'self'
总结:
functools.partialmethod:在类中,调用外部函数时,会自动传类的对象
functools.partial:在类中,调用外部函数时,需要手动传类的对象
5、@functools.wraps,装饰器的使用
importfunctoolsdefshow_details(name, f):"显示一个对象的详细信息"
print('{}:'.format(name))print('object:', f)print('__name__:', end=' ')try:print(f.__name__)exceptAttributeError:print('(no __name__)')print('__doc__', repr(f.__doc__))print()defsimple_decorator(f):
@functools.wraps(f)def decorated(a='decorated defaults', b=1):print('decorated:', (a, b))print(' ', end=' ')return f(a, b=b)returndecorateddef 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_decoratordefdecorated_myfunc(a, b):
myfunc(a, b)returnshow_details('decorated_myfunc', decorated_myfunc)
decorated_myfunc()
decorated_myfunc('args to decorated', 4)
functools_wraps.py
运行效果
myfunc:
object:
__name__: myfunc__doc__ 'myfunc() is not complicated'myfunc: ('unwrapped, default b', 2)
myfunc: ('unwrapped, passing b', 3)
wrapped_myfunc:
object:
__name__: myfunc__doc__ 'myfunc() is not complicated'decorated: ('decorated defaults', 1)
myfunc: ('decorated defaults', 1)
decorated: ('args to wrapped', 4)
myfunc: ('args to wrapped', 4)
decorated_myfunc:
object:
__name__: decorated_myfunc__doc__None
decorated: ('decorated defaults', 1)
myfunc: ('decorated defaults', 1)
decorated: ('args to decorated', 4)
myfunc: ('args to decorated', 4)
6、functools.total_ordering的装饰示例
importfunctoolsimportinspectfrom pprint importpprint
@functools.total_orderingclassMyObject:def __init__(self, val):
self.val=valdef __eq__(self, other):print('testing __eq__({}, {})'.format(
self.val, other.val))return self.val ==other.valdef __gt__(self, other):print('testing __gt__({}, {})'.format(
self.val, other.val))return self.val >other.valprint('Methods:\n')
pprint(inspect.getmembers(MyObject, inspect.isfunction))#获取内置函数的实现对应方法
a= MyObject(1)
b= MyObject(2)print('\nComparisons:')for expr in ['a < b', 'a <= b', 'a == b', 'a >= b', 'a > b']:print('\n{:<6}:'.format(expr))
result=eval(expr)print('result of {}: {}'.format(expr, result))
functools_total_ordering.py
运行效果
Methods:
[('__eq__', ),
('__ge__', ),
('__gt__', ),
('__init__', ),
('__le__', ),
('__lt__', )]
Comparisons:
a
testing__gt__(1, 2)
testing__eq__(1, 2)
result of a
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
总结:
使用functools.total_ordering装饰的时候,必须要实现__eq__和其它的比较方法
7、比较顺序的示例
importfunctoolsclassMyObject:def __init__(self, val):
self.val=valdef __str__(self):return 'MyObject({})'.format(self.val)defcompare_obj(a, b):"""旧式的比较功能。"""
print('comparing {} and {}'.format(a, b))if a.val
elif a.val >b.val:return 1
return0#制用一个key函数,使用cmp_to_key()
get_key =functools.cmp_to_key(compare_obj)defget_key_wrapper(o):"get_key允许打印语句的Wrapper函数."new_key=get_key(o)print('key_wrapper({}) -> {!r}'.format(o, new_key))returnnew_key
objs= [MyObject(x) for x in range(5, 0, -1)]for o in sorted(objs, key=get_key_wrapper):print(o)
functools_cmp_to_key.py
运行效果
key_wrapper(MyObject(5)) -> key_wrapper(MyObject(4)) -> key_wrapper(MyObject(3)) -> key_wrapper(MyObject(2)) -> key_wrapper(MyObject(1)) -> comparing MyObject(4) and MyObject(5)
comparing MyObject(3) and MyObject(4)
comparing MyObject(2) and MyObject(3)
comparing MyObject(1) and MyObject(2)
MyObject(1)
MyObject(2)
MyObject(3)
MyObject(4)
MyObject(5)
8、函数中最近最少使用的缓存查看,删除的示例
importfunctools
@functools.lru_cache()defexpensive(a, b):print('expensive({}, {})'.format(a, b))return a *b
MAX= 2
print('First set of calls:')for i inrange(MAX):for j inrange(MAX):
expensive(i, j)print(expensive.cache_info())print('\nSecond set of calls:')for i in range(MAX + 1):for j in range(MAX + 1):
expensive(i, j)print(expensive.cache_info())print('\nClearing cache:')
expensive.cache_clear()print(expensive.cache_info())print('\nThird set of calls:')for i inrange(MAX):for j inrange(MAX):
expensive(i, j)print(expensive.cache_info())
functools_lru_cache.py
运行效果
First set of calls:
expensive(0, 0)
expensive(0,1)
expensive(1, 0)
expensive(1, 1)
CacheInfo(hits=0, misses=4, maxsize=128, currsize=4)
Second set of calls:
expensive(0,2)
expensive(1, 2)
expensive(2, 0)
expensive(2, 1)
expensive(2, 2)
CacheInfo(hits=4, misses=9, maxsize=128, currsize=9)
Clearing cache:
CacheInfo(hits=0, misses=0, maxsize=128, currsize=0)
Third set of calls:
expensive(0, 0)
expensive(0,1)
expensive(1, 0)
expensive(1, 1)
CacheInfo(hits=0, misses=4, maxsize=128, currsize=4)
9、设置缓存的过期大小示例
importfunctools
@functools.lru_cache(maxsize=2)defexpensive(a, b):print('called expensive({}, {})'.format(a, b))return a *bdefmake_call(a, b):print('({}, {})'.format(a, b), end=' ')
pre_hits=expensive.cache_info().hits
expensive(a, b)
post_hits=expensive.cache_info().hitsif post_hits >pre_hits:print('cache hit')print('Establish the cache')
make_call(1, 2)
make_call(2, 3)print('\nUse cached items')
make_call(1, 2)
make_call(2, 3)print('\nCompute a new value, triggering cache expiration')
make_call(3, 4)print('\nCache still contains one old item')
make_call(2, 3)print('\nOldest item needs to be recomputed')
make_call(1, 2)
functools_lru_cache_expire.py
运行效果
Establish the cache
(1, 2) called expensive(1, 2)
(2, 3) called expensive(2, 3)
Use cached items
(1, 2) cache hit
(2, 3) cache hit
Compute a new value, triggering cache expiration
(3, 4) called expensive(3, 4)
Cache still contains one old item
(2, 3) cache hit
Oldest item needs to be recomputed
(1, 2) called expensive(1, 2)
10、列表和字典不可散列运算的示例
importfunctools
@functools.lru_cache(maxsize=2)defexpensive(a, b):print('called expensive({}, {})'.format(a, b))return a *bdefmake_call(a, b):print('({}, {})'.format(a, b), end=' ')
pre_hits=expensive.cache_info().hits
expensive(a, b)
post_hits=expensive.cache_info().hitsif post_hits >pre_hits:print('cache hit')
make_call(1, 2)try:
make_call([1], 2)exceptTypeError as err:print('ERROR: {}'.format(err))try:
make_call(1, {'2': 'two'})exceptTypeError as err:print('ERROR: {}'.format(err))
functools_lru_cache_arguments.py
运行效果
(1, 2) called expensive(1, 2)
([1], 2) ERROR: unhashable type: 'list'(1, {'2': 'two'}) ERROR: unhashable type: 'dict'
11、functools.reduce递归求指定序列值的和
importfunctoolsdefdo_reduce(a, b):print('do_reduce({}, {})'.format(a, b))return a +b
data= range(1, 5)print(data)
result=functools.reduce(do_reduce, data)print('result: {}'.format(result))
functools_reduce.py
运行效果
range(1, 5)
do_reduce(1, 2)
do_reduce(3, 3)
do_reduce(6, 4)
result:10
12、指定初始化值,递归求和的示例
importfunctoolsdefdo_reduce(a, b):print('do_reduce({}, {})'.format(a, b))return a +b
data= range(1, 5)print(data)
result= functools.reduce(do_reduce, data, 99)print('result: {}'.format(result))
functools_reduce_initializer.py
运行效果
range(1, 5)
do_reduce(99, 1)
do_reduce(100, 2)
do_reduce(102, 3)
do_reduce(105, 4)
result:109
13、支持列表递归求和的示例
importfunctoolsdefdo_reduce(a, b):print('do_reduce({}, {})'.format(a, b))return a +bprint('Single item in sequence:', functools.reduce(do_reduce, [1]))print('Single item in sequence with initializer:', functools.reduce(do_reduce, [1,2], 99))print('Empty sequence with initializer:', functools.reduce(do_reduce, [], 99))try:print('Empty sequence:', functools.reduce(do_reduce, []))exceptTypeError as err:print('ERROR: {}'.format(err))
functools_reduce_short_sequences.py
运行效果
Single item in sequence: 1do_reduce(99, 1)
do_reduce(100, 2)
Single itemin sequence with initializer: 102Empty sequence with initializer:99ERROR: reduce() of empty sequence with no initial value
14、泛型函数,根据函数基本类型形参调用不同的函数
importfunctools
@functools.singledispatchdefmyfunc(arg):print('default myfunc({!r})'.format(arg))
@myfunc.register(int)defmyfunc_int(arg):print('myfunc_int({})'.format(arg))
@myfunc.register(list)defmyfunc_list(arg):print('myfunc_list()')for item inarg:print('{}'.format(item))
myfunc('string argument')
myfunc(1)
myfunc(2.3)
myfunc(['a', 'b', 'c'])
functools_singledispatch.py
运行效果
default myfunc('string argument')
myfunc_int(1)
default myfunc(2.3)
myfunc_list()
a
b
c
15、泛型函数,根据函数自己定义类的形参调用不用的函数
importfunctoolsclassA:pass
classB(A):pass
classC(A):pass
classD(B):pass
classE(C, D):[email protected](arg):print('default myfunc({})'.format(arg.__class__.__name__))
@myfunc.register(A)defmyfunc_A(arg):print('myfunc_A({})'.format(arg.__class__.__name__))
@myfunc.register(B)defmyfunc_B(arg):print('myfunc_B({})'.format(arg.__class__.__name__))
@myfunc.register(C)defmyfunc_C(arg):print('myfunc_C({})'.format(arg.__class__.__name__))
myfunc(A())
myfunc(B())
myfunc(C())
myfunc(D())
myfunc(E())
functools_singledispatch_mro.py
运行效果
myfunc_A(A)
myfunc_B(B)
myfunc_C(C)
myfunc_B(D)
myfunc_C(E)