Python之functools模块的使用
functools模块的作用
functools模块提供了一些工具来调整或扩展函数和其他callable对象,从而不必完全重写。
1、functools.partial,给函数默认传参,再使用函数一样的调用方式的示例
import functools def 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 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) # 运行结果 # myfunc: # object: <function myfunc at 0x000001E15CD045E8> # __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(<function myfunc at 0x000002720F1D4678>, b=4) # func: <function myfunc at 0x000002720F1D4678> # 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(<function myfunc at 0x000002838A8445E8>, 'default a', b=99) # func: <function myfunc at 0x000002838A8445E8> # 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'
运行效果
TypeError: myfunc() missing 1 required positional argument: 'a' myfunc: object: <function myfunc at 0x000001F5877045E8> __name__: myfunc called myfunc with: ('a', 3) partial with named default: object: functools.partial(<function myfunc at 0x000001F5877045E8>, b=4) func: <function myfunc at 0x000001F5877045E8> args: () keywords: {'b': 4} called myfunc with: ('passing a', 4) called myfunc with: ('override b', 5) partial with defaults: object: functools.partial(<function myfunc at 0x000001F5877045E8>, 'default a', b=99) func: <function myfunc at 0x000001F5877045E8> args: ('default a',) keywords: {'b': 99} called myfunc with: ('default a', 99) called myfunc with: ('default a', 'override b') 参数缺少
2、更新被functools.partial装饰过的的内置属性值
import functools def 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__) except AttributeError: print('(no __name__)') print(' __doc__', repr(f.__doc__)) print() show_details('myfunc', myfunc) # myfunc: 信息输出正常 # object: <function myfunc at 0x000001BDCE624678> # __name__: myfunc # __doc__ '\n 定义myfunc函数,打印变量值\n ' p1 = functools.partial(myfunc, b=4) show_details('raw wrapper', p1) # raw wrapper: 装饰过,函数名和 # object: functools.partial(<function myfunc at 0x0000025E5A834678>, 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(<function myfunc at 0x000001B3E7B84678>, b=4) # __name__: myfunc # __doc__ '\n 定义myfunc函数,打印变量值\n '
运行效果
myfunc: object: <function myfunc at 0x000001B3E7B84678> __name__: myfunc __doc__ '\n 定义myfunc函数,打印变量值\n ' raw wrapper: object: functools.partial(<function myfunc at 0x000001B3E7B84678>, 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 0x000001B3E7B84678>, b=4) __name__: myfunc __doc__ '\n 定义myfunc函数,打印变量值\n '
3、functools.partial装饰类,并且修改内置的属性
import functools class MyClass: "模拟工具示范课" 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('{}:'.format(name)) print(' object:', f) print(' __name__:', end=' ') try: print(f.__name__) except AttributeError: print('(no __name__)') print(' __doc__', repr(f.__doc__)) return o = 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()
运行效果
instance: object: <__main__.MyClass object at 0x000001C73ABFDFC8> __name__: (no __name__) __doc__ '模拟工具示范课' called object with: (<__main__.MyClass object at 0x000001C73ABFDFC8>, 'e goes here', 6) instance wrapper: object: functools.partial(<__main__.MyClass object at 0x000001C73ABFDFC8>, e='default for e', f=8) __name__: (no __name__) __doc__ '模拟工具示范课' called object with: (<__main__.MyClass object at 0x000001C73ABFDFC8>, 'default for e', 8)
4、functools.partialmethod与functools.partial的区别
import functools def standalone(self, a=1, b=2): "Standalone function" print(' called standalone with:', (self, a, b)) if self is not None: print(' self.attr =', self.attr) class MyClass: "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() except TypeError as err: print('ERROR: {}'.format(err))
运行效果
standalone called standalone with: (None, 1, 2) method1 as partialmethod called standalone with: (<__main__.MyClass object at 0x0000024184B0DE08>, 1, 2) self.attr = instance attribute method2 as partial ERROR: standalone() missing 1 required positional argument: 'self'
总结:
functools.partialmethod:在类中,调用外部函数时,会自动传类的对象
functools.partial:在类中,调用外部函数时,需要手动传类的对象
5、@functools.wraps,装饰器的使用
import functools def show_details(name, f): "显示一个对象的详细信息" print('{}:'.format(name)) print(' object:', f) print(' __name__:', end=' ') try: print(f.__name__) except AttributeError: print('(no __name__)') print(' __doc__', repr(f.__doc__)) print() def simple_decorator(f): @functools.wraps(f) def decorated(a='decorated defaults', b=1): print(' decorated:', (a, b)) print(' ', end=' ') return f(a, b=b) 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)
运行效果
myfunc: object: <function myfunc at 0x0000023E36C23708> __name__: myfunc __doc__ 'myfunc() is not complicated' myfunc: ('unwrapped, default b', 2) myfunc: ('unwrapped, passing b', 3) wrapped_myfunc: object: <function myfunc at 0x0000023E36C23678> __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: <function decorated_myfunc at 0x0000023E36C233A8> __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的装饰示例
import functools import inspect from pprint import pprint @functools.total_ordering class MyObject: def __init__(self, val): self.val = val def __eq__(self, other): print(' testing __eq__({}, {})'.format( self.val, other.val)) return self.val == other.val def __gt__(self, other): print(' testing __gt__({}, {})'.format( self.val, other.val)) return self.val > other.val print('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))
运行效果
Methods: [('__eq__', <function MyObject.__eq__ at 0x0000020AB4282A68>), ('__ge__', <function _ge_from_gt at 0x0000020AB403DD38>), ('__gt__', <function MyObject.__gt__ at 0x0000020AB4282708>), ('__init__', <function MyObject.__init__ at 0x0000020AB40EE318>), ('__le__', <function _le_from_gt at 0x0000020AB403DDC8>), ('__lt__', <function _lt_from_gt at 0x0000020AB403D0D8>)] 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
总结:
使用functools.total_ordering装饰的时候,必须要实现__eq__和其它的比较方法
7、比较顺序的示例
import functools class MyObject: def __init__(self, val): self.val = val def __str__(self): return 'MyObject({})'.format(self.val) def compare_obj(a, b): """ 旧式的比较功能。 """ print('comparing {} and {}'.format(a, b)) if a.val < b.val: return -1 elif a.val > b.val: return 1 return 0 # 制用一个key函数,使用cmp_to_key() get_key = functools.cmp_to_key(compare_obj) def get_key_wrapper(o): "get_key允许打印语句的Wrapper函数." new_key = get_key(o) print('key_wrapper({}) -> {!r}'.format(o, new_key)) return new_key objs = [MyObject(x) for x in range(5, 0, -1)] for o in sorted(objs, key=get_key_wrapper): print(o)
运行效果
key_wrapper(MyObject(5)) -> <functools.KeyWrapper object at 0x0000025797BBBB50> key_wrapper(MyObject(4)) -> <functools.KeyWrapper object at 0x0000025797BBBB30> key_wrapper(MyObject(3)) -> <functools.KeyWrapper object at 0x0000025797BBBB10> key_wrapper(MyObject(2)) -> <functools.KeyWrapper object at 0x0000025797BBBAF0> key_wrapper(MyObject(1)) -> <functools.KeyWrapper object at 0x0000025797BBBA90> 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、函数中最近最少使用的缓存查看,删除的示例
import functools @functools.lru_cache() def expensive(a, b): print('expensive({}, {})'.format(a, b)) return a * b MAX = 2 print('First set of calls:') for i in range(MAX): for j in range(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 in range(MAX): for j in range(MAX): expensive(i, j) print(expensive.cache_info())
运行效果
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、设置缓存的过期大小示例
import functools @functools.lru_cache(maxsize=2) def expensive(a, b): print('called expensive({}, {})'.format(a, b)) return a * b def make_call(a, b): print('({}, {})'.format(a, b), end=' ') pre_hits = expensive.cache_info().hits expensive(a, b) post_hits = expensive.cache_info().hits if 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)
运行效果
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、列表和字典不可散列运算的示例
import functools @functools.lru_cache(maxsize=2) def expensive(a, b): print('called expensive({}, {})'.format(a, b)) return a * b def make_call(a, b): print('({}, {})'.format(a, b), end=' ') pre_hits = expensive.cache_info().hits expensive(a, b) post_hits = expensive.cache_info().hits if post_hits > pre_hits: print('cache hit') make_call(1, 2) try: make_call([1], 2) except TypeError as err: print('ERROR: {}'.format(err)) try: make_call(1, {'2': 'two'}) except TypeError as err: print('ERROR: {}'.format(err))
运行效果
(1, 2) called expensive(1, 2) ([1], 2) ERROR: unhashable type: 'list' (1, {'2': 'two'}) ERROR: unhashable type: 'dict'
11、functools.reduce递归求指定序列值的和
import functools def do_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))
运行效果
range(1, 5) do_reduce(1, 2) do_reduce(3, 3) do_reduce(6, 4) result: 10
12、指定初始化值,递归求和的示例
import functools def do_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))
运行效果
range(1, 5) do_reduce(99, 1) do_reduce(100, 2) do_reduce(102, 3) do_reduce(105, 4) result: 109
13、支持列表递归求和的示例
import functools def do_reduce(a, b): print('do_reduce({}, {})'.format(a, b)) return a + b print('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, [])) except TypeError as err: print('ERROR: {}'.format(err))
运行效果
Single item in sequence: 1 do_reduce(99, 1) do_reduce(100, 2) Single item in sequence with initializer: 102 Empty sequence with initializer: 99 ERROR: reduce() of empty sequence with no initial value
14、泛型函数,根据函数基本类型形参调用不同的函数
import functools @functools.singledispatch def myfunc(arg): print('default myfunc({!r})'.format(arg)) @myfunc.register(int) def myfunc_int(arg): print('myfunc_int({})'.format(arg)) @myfunc.register(list) def myfunc_list(arg): print('myfunc_list()') for item in arg: print(' {}'.format(item)) myfunc('string argument') myfunc(1) myfunc(2.3) myfunc(['a', 'b', 'c'])
运行效果
default myfunc('string argument') myfunc_int(1) default myfunc(2.3) myfunc_list() a b c
15、泛型函数,根据函数自己定义类的形参调用不用的函数
import functools class A: pass class B(A): pass class C(A): pass class D(B): pass class E(C, D): pass @functools.singledispatch def myfunc(arg): print('default myfunc({})'.format(arg.__class__.__name__)) @myfunc.register(A) def myfunc_A(arg): print('myfunc_A({})'.format(arg.__class__.__name__)) @myfunc.register(B) def myfunc_B(arg): print('myfunc_B({})'.format(arg.__class__.__name__)) @myfunc.register(C) def myfunc_C(arg): print('myfunc_C({})'.format(arg.__class__.__name__)) myfunc(A()) myfunc(B()) myfunc(C()) myfunc(D()) myfunc(E())
运行效果
myfunc_A(A)
myfunc_B(B)
myfunc_C(C)
myfunc_B(D)
myfunc_C(E)