• 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,然后会把初始值带入到以后的计算 ;
    
    '''
  • 相关阅读:
    黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战a基础篇
    单点登录业务逻辑解析
    C++ do{ } while(0)
    esp8266+MQTT+DHT11(温湿度计) platformio
    ESP8266+ MQTT+SG90(舵机) platformio
    esp8266+http (PlatformIO)
    ESP32+L298N+MQTT+4G无线远程监控+四驱动小破车
    ESP8266 + MQTT + 土壤湿度传感器
    esp8266 + mqtt + 温度计 (platformio)
    根据配置生成表格布局
  • 原文地址:https://www.cnblogs.com/soulgou123/p/9648229.html
Copyright © 2020-2023  润新知