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)