1、装饰器
1.1、什么是装饰器?
# 装饰器(Decorator):从字面意思理解,就是装饰对象的工具;装饰器也是闭包函数+高阶函数的一种应用;
# 装饰器的使用原则:
①:不修改被装饰对象的源代码;
②:不改变被修饰对象的调用方式;
③:在满足前面两个条件的前提下,为被装饰对象添加上新功能;
1.2、装饰器语法糖
# ‘@deco’ 是pyhton提供的一种语法糖;
def deco(fn): def warrper(*args,**kwargs): print('add to decorater..') res=fn(*args,**kwargs) return res return warrper ''' 装饰器也是函数对象 @deco为python提供的一种语法糖,其等价式为 add=deco(add), 使用此语法糖,会将@的下一行定义的函数名作为参数传递给装饰器函数 ''' @deco def add(x,y): return x+y print(add(1,2)) ''' 运行结果 add to decorater.. 3 '''
# 多装饰器语法:
被装饰函数的正上方,单独一行 @deco1 @deco2 @deco3 def foo(): pass foo=deco1(deco2(deco3(foo)))
# 多装饰器装饰一个函数对象实例分析:
def a(func): print('Get in decorator_a') #print(id(func), '-' * 30, 'from a') def inner_a(*args, **kwargs): #print(id(func),'-'*30,'from inner_a') print('Get in inner_a') return func(*args, **kwargs) return inner_a
def b(func): print('Get in decorator_b') #print(id(func), '-' * 30, 'from b') def inner_b(*args, **kwargs): #print(id(func),'-'*30,'from inner_b') print('Get in inner_b') return func(*args, **kwargs) return inner_b
def c(func): print('Get in decorator_c') def inner_c(*args, **kwargs): print('Get in inner_c') return func(*args, **kwargs) return inner_c @c @b# @a# f=c(b(a(f))) def f(x): print(id(f),'-'*30,'from f') print('Get in f') return x * 2 f(1) ''' @b @a def f(x):pass ===>f=b(a(f)) 打印deco_a ==> f=b(inner_a)=inner_b 打印deco_b ==> f=inner_b, f(x)==> inner_b(x) ==> 打印inner_b ==> 执行inner_b中的return func(*args, **kwargs) ,由于这个func来自于b(func), b中记录的func=inner_a == >执行inner_a 打印inner_a, 打印完后执行return func(*args, **kwargs),此时的func来自于a(func),
所以此时执行真正的要被执行的函数f, 所以顺序为: deco_a deco_b inner_b inner_a f ''' ''' 运行结果: Get in decorator_a Get in decorator_b Get in decorator_c Get in inner_c Get in inner_b Get in inner_a 2410916742008 ------------------------------ from f Get in f '''
1.3、有参装饰器
# 有参数装饰器:其实就是对装饰器函数就行柯里化,柯里化后第一层包装必须返回的是可调用的对象;
# 假如有一下代码: @decorator(x, y, z) def func(a, b): pass 装饰器处理过程跟下面的调用是等效的; def func(a, b): pass func = decorator(x, y, z)(func) #通过对上面的装饰器进行柯里化 decorator(x, y, z) 的返回结果必须是一个可调用对象,它接受一个函数作为参数并包装它; # 具体实现: def decorator(x,y,z): def warrper(fn): def inner(*args,**kwargs): print(x,y,z) # 可以利用x,y,z变量添加新功能 res=fn(*args,**kwargs) return res return inner return warrper @decorator(10, 20, 30) def add(a, b): return a+b print(add(10,20))
1.4、使用装饰器后保留被装饰函数的元信息
# 使用装饰器装饰某个函数之后,这个函数的重要的元信息,例如:名字,文档字符串,注解和参数签名都会被改变;
# 任何时候你定义装饰器的时候,都应该使用 functools
库中的 @wraps
装饰器来注解底层包装函数。例如:
1 from functools import wraps 2 3 def decorator(x,y,z): 4 def warrper(fn): 5 @wraps(fn) # 将fn的元信息替换回去 6 def inner(*args,**kwargs): 7 print(x,y,z) # 可以利用x,y,z变量添加新功能 8 res=fn(*args,**kwargs) 9 return res 10 return inner 11 return warrper 12 13 @decorator(10, 20, 30) 14 def add(a, b): 15 return a+b 16 17 print(add(10,20)) 18 print(add.__name__) 19 20 ''' 21 运行结果: 22 10 20 30 23 30 24 add 25 '''
2、偏函数
# 偏函数:可以减少函数的参数个数;可以使用 functools.partial()
;partial()
函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数;
# 具体实现解析:
# from functools import partial # python提供的partial函数 # 自定义partial函数 def partial(func,*args,**kwargs): def newfunc(*fargs,**fkeywords): newkeywords= kwargs.copy() # 拷贝kwargs到newkeywords newkeywords.update(fkeywords) return func(*(args+fargs),**newkeywords) # 解构参数列表 newfunc.func=func # 保留原函数 newfunc.args=args # 保留原来的位置参数 newfunc.kwargs=kwargs # 保留原来的关键字参数 return newfunc def add(x,y,z): return x+y+z newadd=partial(add,z=1) ''' partial(add,z=1) ==> func=add , args=(),kwargs={'z':1} newadd=partial(add,z=1)=newfunc ==> newadd(2,3) ==> newfunc(2,3) ==> fargs=(2,3) ,fkeywords={} newfunc.func=func=add newfunc.args=args=() newfunc.kwargs=kwargs={'z':1} ==> newkeywords=kwargs.copy() ==> newkeywords={'z':1} newkeywords.update(fkeywords)==> newkeywords.update({}) func(*(args+fargs),**newkeywords) ==> args+fargs=()+(2,3)=(2,3) newkeywords={'z':1} ==> add(*(2,3),**{'z':1}) ==> add(2,3,z=1) ''' print(newadd(2,3))
# update_wrapper与wraps分析
from functools import partial WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__') # 函数的签名信息 WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, # wrapper包装函数 wrapped, # 被包装函数(被装饰的函数) assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): for attr in assigned: try: value = getattr(wrapped, attr) # value=wrapped.attr ==>从被装饰函数里面get属性,赋值给value except AttributeError: pass else: setattr(wrapper, attr, value) # wrapper.attr=value ==>将value赋值给装饰函数的属性attr即wrapper.attr # 上面循环完成从被装饰函数get属性的值,赋值给装饰函数的属性,就是一个属性覆盖的过程 for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # 对象字典的更新,将被装饰函数的字典更新到装饰函数的字典 wrapper.__wrapped__ = wrapped # 动态的给装饰器函数添加一个属性 return wrapper def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) ''' partial对update_wrapper函数进行从新封装,把该函数由原来的2参函数变成1参数函数 原来调用方式:update_wrapper(wrapper,wrapperd) 封装后调用:wraps(wrapped)(wrapper),其中wraps(wrapped)返回为对update_wrapper进行封装后的新函数, 可以理解为将update_wrapper封装成了装饰器函数 用法: @wraps(fn) ==> w=wraps(fn)(w) def w:pass '''
3、参数注解
3.1、 参数注解:
①:python3.5引入
②:对函数的参数进行类型注解
③:对函数的返回值进行类型注解
④:只对函数参数做一个辅助的说明,并不对函数参数进行类型检查;
⑤:函数注解的信息,保存在__annotiation__属性中;
3.2、inspet模块
# inspet.signature(calleable) 获取函数签名 (函数的签名包含了一个函数的信息,包含函数名,它的参数类型,它所在的类和名称空间以及其他信息)
## inspect.signature
def add(x: int, y: int=4, *args, **kwargs) -> int: return x + y sig = inspect.signature(add) # 获取add函数的签名,将签名信息保存在一个元组中; print(sig) # (x:int, y:int, *args, **kwargs) -> int print(type(sig)) # sig类型为:<class 'inspect.Signature'> params = sig.parameters # 得到一个有序字典orderDict; print(params) ''' OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)]) ''' for k,v in params.items(): print(v.name,v.default,v.annotation,v.kind) ''' 打印的顺序是跟参数列表的顺序一致 key----->value -----> type(value) x x:int <class 'inspect.Parameter'> y y:int <class 'inspect.Parameter'> args *args <class 'inspect.Parameter'> kwargs **kwargs <class 'inspect.Parameter'> v.name------> v.default ----> v.annotation -------------> v.kind x <class 'inspect._empty'> <class 'int'> POSITIONAL_OR_KEYWORD y <class 'inspect._empty'> <class 'int'> POSITIONAL_OR_KEYWORD args <class 'inspect._empty'> <class 'inspect._empty'> VAR_POSITIONAL kwargs <class 'inspect._empty'> <class 'inspect._empty'> VAR_KEYWORD v.name ==> 参数的名字 v.default ==> 参数的缺省值,可能没定义; 例如(x,y=4) ,此时则为4; v.annotation ==> 参数注解,可能没定义; empty, ==> 特殊的类,用来标记default属性或者注释annotation属性的空值 v.kind, ==> 实参如何绑定到形参数,就是形参的类型; 例如:VAR_POSITIONAL:位置参数,VAR_KEYWORD:关键字参数; ''' print('return',sig.return_annotation) # ==> return <class 'int'> return的返回类型print(params['x'],type(params['x'])) # ==> x:int <class 'inspect.Parameter'> print(params['x'].annotation,type(params['x'].annotation)) # ==><class 'int'> <class 'type'>
3.3、实现一个函数参数类型检查的装饰器
1 import inspect 2 import time 3 def checkType(fn): 4 def warpper(*args, **kwargs): 5 sig = inspect.signature(fn) 6 params = sig.parameters # 有序字典orderDict 7 values = list(params.values()) 8 for i, x in enumerate(args): 9 if values[i].annotation != inspect._empty and not isinstance(x, values[i].annotation): 10 print(x, '-->不合法') 11 break 12 else: 13 print(x, 'ok--->') 14 for k, v in kwargs: 15 print(v, params[k], params[k].annotation) 16 if params[k].annotation != inspect._empty and not isinstance(v, params[k].annotation): 17 print(v, '不合法') 18 break 19 else: 20 print(v, 'ok!!!!') 21 for k, v in params.items(): 22 print(k, v.name, v.default, v.kind, v.annotation) 23 ret = fn(*args, **kwargs) 24 return ret 25 return warpper 26 27 @checkType 28 def add(x: int, y: int) -> int: 29 ''' 30 this is add func 31 ''' 32 time.sleep(2) 33 return x + y
4、functools
4.1、functools.reduce
# reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算;
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4) ''' reduce源码如下: ''' def reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value def foo(value,element): return value+element print(reduce(foo,range(5))) ''' function=foo iterable=it=iter(range(5)) initializer=None, 第一个value=next(it) ==> value=0,然后会把初始值带入到以后的计算 ; '''