Python学习week6-装饰器

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,然后会把初始值带入到以后的计算 ;

'''

 

posted @ 2018-09-14 18:24  soulgou  阅读(263)  评论(0)    收藏  举报