zoukankan      html  css  js  c++  java
  • 装饰器本质以及装饰器产生的问题

    装饰器介绍

    装饰器的本质:一个闭包函数

    装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

    一个简单的装饰器实例

    def time(func):#定义一个装饰器,接收一个函数作为参数
        def inner(*args,**kwargs):#在装饰器内定义一个内部函数
            """执行函数之前要做的"""
            re = func(*args,**kwargs)#被装饰的函数并且要执行,并接受结果
            """执行函数之后要做的"""
            return re#返回原函数的运行结果
        return inner#返回内部函数名
    
    def func():
        pass
    func = time(func) # 此时func ==inner
    

    python为我们提供了简单写法

    def time(func):
        def inner(*args,**kwargs):
            """执行函数之前要做的"""
            re = func(*args,**kwargs)
            """执行函数之后要做的"""
            return re#返回原函数的运行结果
        return inner
    
    @time
    def func():
        pass
    # @time就等于func = time(func),下面要紧接函数
    #当再次调用函数func是实际调用的是inner
    #接收到的参数会被inner(*args,**kwargs)接受,并存储到元祖或字典中
    #而args或者kwargs在传递到原函数时又会被分解
    #从而实现了不改变函数调用方式,添加了新功能
    

    当我们明白装饰器的本质之后,就可以去搞搞更多的装饰器,比如带参数的装饰器,以及用类写一个装饰器

    带参数的函数装饰器:@xxx()就相当于xxx()的返回结果还可以接受一个函数被调用,然后再返回一个函数

    def deco(arg):  
        def _deco(func):  
            def __deco():  
                print("before %s called [%s]." % (func.__name__, arg))  
                func()  
                print("  after %s called [%s]." % (func.__name__, arg))  
            return __deco  
        return _deco  
     
    @deco("mymodule")  
    def myfunc():  
        print(" myfunc() called.")  
    

    用类实现一个装饰器:func = Clsaa(func),func()就是执行了Class中的__call__

    class ttl_func:
        def __init__(self,ttl_property):
            self.ttl_property=ttl_property
        def __call__(self, *args, **kwargs):
              # 函数执行前做的
              ret = self.ttl_property( *args, **kwargs)
              # 函数执行后做的
              return ret
    @ttl_property
    def cc():
        print(time.time())

    装饰器产生的问题

    def outer(func):
        def inner(*args,**kwargs):
            """我是装饰器里的函数"""
            func(*args,**kwargs)
        return inner
     
    @outer
    def function():
        """我是被装饰的函数"""
        print("哈哈哈")
     
    print(function.__name__) # 函数名
    print(function.__doc__)  # 函数注释
    # 打印了装饰其中的内容,function其实就是outer(function) 就是inner函数
    # inner
    # 我是装饰器里的函数
     
    # 修复方法
    from functools import wraps
    def outer(func):
        @wraps(func)
        def inner(*args,**kwargs):
            """我是装饰器里的函数"""
            func(*args,**kwargs)
        return inner
     
    @outer
    def function():
        """我是被装饰的函数"""
        print("哈哈哈")
     
    print(function.__name__) # 函数名
    print(function.__doc__)  # 函数注释
     
    #function
    #我是被装饰的函数
    

    来了解一下wraps是如何做到的,首先我们要介绍一下partial和update_wrapper函数

    functools.partial函数

    该函数接收一个函数func和一些位置参数和关键字参数,内部会将这些参数绑定给func并返回一个新的函数,如果一个关键字参数被重复传入,后面的值会覆盖前面的值

    from functools import partial
    
    def add(x, y):
        return x+y
    
    # 关键字传入
    add2 = partial(add, y=2)
    add3 = partial(add2, y=3)
    q = add2(3)  # 这里将会输出5
    p = add3(3)  # 这里将会输出6
    
    # 位置传入
    add4 = partial(add, 2)
    add5 = partial(add4, 3)
    b = add2(3)  # 这里将会输出5
    d = add3()  # 这里将会输出5
    

      这个函数是使用C而不是Python实现的,但是官方文档中给出了Python实现的代码,如下所示,大家可以进行参考

    def partial(func, *args, **keywords): # 设置的参数
        def newfunc(*fargs, **fkeywords): # 调用的参数传入
            newkeywords = keywords.copy()
            newkeywords.update(fkeywords) # 关键字参数的更新
            return func(*args, *fargs, **newkeywords)
        newfunc.func = func
        newfunc.args = args
        newfunc.keywords = keywords
        return newfunc
    

    functools.update_wrapper

    源码

    WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                           '__annotations__')
    WRAPPER_UPDATES = ('__dict__',)
    def update_wrapper(wrapper,
                       wrapped,
                       assigned = WRAPPER_ASSIGNMENTS,
                       updated = WRAPPER_UPDATES):
        for attr in assigned:
            try:
                value = getattr(wrapped, attr) # 从被修饰的函数中获得指定属性
            except AttributeError:
                pass
            else:
                setattr(wrapper, attr, value) # 设置给修饰他的函数
        for attr in updated:
            getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # 更新修饰字典的键值
        wrapper.__wrapped__ = wrapped
        return wrapper #返回修饰函数
    

    使用update_wrapper来修复一个修饰器函数

    from functools import update_wrapper
    
    def wrapper(f):
        def wrapper_function(*args, **kwargs):
            """这个是修饰函数"""
            return f(*args, **kwargs)
        update_wrapper(wrapper_function, f)  # << 此处修复
        return wrapper_function
        
    @wrapper
    def wrapped():
        """这个是被修饰的函数"""
        print('wrapped')
    
    
    print(wrapped.__doc__)  # 输出`这个是被修饰的函数`
    print(wrapped.__name__)  # 输出`wrapped`
    

    functools.wraps

    源码:

    def wraps(wrapped,
              assigned = WRAPPER_ASSIGNMENTS,
              updated = WRAPPER_UPDATES):
        return partial(update_wrapper, wrapped=wrapped,
                       assigned=assigned, updated=updated)
    @wraps
    def func():
        pass
    # func = wraps(func) = partial(update_wrapper, wrapped=func,assigned=assigned, updated=updated) = 一个绑定好属性的函数
    

      

  • 相关阅读:
    「ZJOI2019」开关
    「ZJOI2019」Minimax 搜索
    杨氏矩阵学习笔记
    「LibreOJ NOI Round #2」简单算术
    「LibreOJ NOI Round #2」小球进洞
    组合总和 II(力扣第40题)
    组合总和 I(力扣第39题)
    组合(力扣第77题)
    使用MapReduce解决蚂蚁森林第二题
    Hive练习--蚂蚁森林习题二
  • 原文地址:https://www.cnblogs.com/wwg945/p/8922235.html
Copyright © 2011-2022 走看看