天天看點

python functools_Python之functools子產品的使用

functools子產品的作用

functools子產品提供了一些工具來調整或擴充函數和其他callable對象,進而不必完全重寫。

1、functools.partial,給函數預設傳參,再使用函數一樣的調用方式的示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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裝飾過的的内置屬性值

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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裝飾類,并且修改内置的屬性

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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的差別

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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,裝飾器的使用

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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的裝飾示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、比較順序的示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、函數中最近最少使用的緩存檢視,删除的示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、設定緩存的過期大小示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、清單和字典不可散列運算的示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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遞歸求指定序列值的和

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、指定初始化值,遞歸求和的示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、支援清單遞歸求和的示例

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、泛型函數,根據函數基本類型形參調用不同的函數

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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、泛型函數,根據函數自己定義類的形參調用不用的函數

python functools_Python之functools子產品的使用
python functools_Python之functools子產品的使用

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)