装饰器小记

装饰器

斐波那契数列+ 装饰器

  1. 不加装饰器
    def fib(n):
        if n <= 1: return 1
        return fib(n - 1) + fib(n - 2)
    
    start = time.time()
    print(fib(40)) #165580141
    end = time.time()
    print(f'finished in {end - start} second.') #finished in 32.83299708366394 second.
    
  2. 加装饰器
    class myCache:
        def __init__(self, func):
            self.func = func
            self.cache = {}
    
        def __call__(self, *args):
            if args not in self.cache:
                self.cache[args] = self.func(*args)
            return self.cache[args]
    @myCache
    def fib(n):
        if n <= 1: return 1
        return fib(n - 1) + fib(n - 2)
    
    start = time.time()
    print(fib(100)) # 573147844013817084101
    end = time.time()
    print(f'finished in {end - start} second.') # finished in 0.0010004043579101562 second.
    

将执行函数以参数方式传到装饰器中
可以明显看到加装饰器以后速度很快,不使用装饰器使用dict做原理也是一样的

django中出现的装饰器总结

1. functools.partial

django 用法:

  • 代码出现的地方

    path = partial(_path, Pattern=RoutePattern)
    re_path = partial(_path, Pattern=RegexPattern)
    
  • 详解

    # 调用path时相当于调用_path('login/', LoginView.as_view(), Pattern=RoutePattern)
    urlpatterns = [
        path('login/', LoginView.as_view()),
    ]
    

2. functools.lru_cache

django:

  • manage.py 命令行执行时fetch_command函数调用get_commands

  • get_commands每次调用都会返回一个dict,当settings文件没有改变的时,返回的值是不变的,使用装饰器则减少了每次启动服务计算commands的时间

    @functools.lru_cache(maxsize=None)
    def get_commands():
        commands = {name: 'django.core' for name in find_commands(__path__[0])}
    
        if not settings.configured:
            return commands
    
        for app_config in reversed(list(apps.get_app_configs())):
            path = os.path.join(app_config.path, 'management')
            commands.update({name: app_config.name for name in find_commands(path)})
    
        return commands
    
  • functools.lru_cache(maxsize=128, typed=False)

    -- maxsize代表缓存的内存占用值,超过这个值之后,之前的结果就会被释放,然后将新的计算结果进行缓存,其值应当设为2的幂
    -- typed若为True,则会把不同的参数类型得到的结果分开保存
    
  • 作用:主要是用来做临时缓存,把耗时的操作结果暂时保存,避免重复计算,比如生成斐波那契数列时,函数后以为结果依赖前两位计算结果值

  • 使用案例:leetcode 329. 矩阵中的最长递增路径,对路径遍历的时候每个点最多上下左右四种情况,使用装饰器避免相同计算

    给定一个整数矩阵,找出最长递增路径的长度。
    对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。
    import functools
    class Solution:
    def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
        @functools.lru_cache(maxsize=None)
        def inspect(i, j):
            res = 0
            if i-1 >=0 and matrix[i-1][j] > matrix[i][j]:
                res =  inspect(i-1,j)
            if i+1 <len(matrix) and matrix[i+1][j] > matrix[i][j]:
                res = inspect(i+1,j)
            if j-1 >= 0 and matrix[i][j-1] > matrix[i][j]:
                res = inspect(i,j-1)
            if j+1 < len(matrix[0]) and matrix[i][j+1] > matrix[i][j]:
                res = inspect(i, j+1)
            return res+1
        lenght = 0
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                lenght = max(lenght, inspect(i,j))
        return lenght
    

3. classonlymethod 和 classmethod

  • django CBV 中 类继承View类,urlpattern里面调用as_view实现一个接口不同不同调用方式走不同的逻辑,as_view方法使用@classonlymethod装饰器
  • 源码
     class classonlymethod(classmethod):
         def __get__(self, instance, cls=None):
             if instance is not None:
                 raise AttributeError("This method is available only on the class, not on instances.")
             return super().__get__(instance, cls)
    
  • 源码可以看出classonlymethod和classmethod的区别即为,classonlymethod只能由类调用,实例对象调用时会抛出对象异常

4. @functools.wraps(func) 用于定义装饰器的时候,特别是多个函数被装饰器装饰时,保留原函数的名称和属性

  • 使用示例:
     def my_decorator(func):      
         @functools.wraps(func)
         def wrapper(*args, **kwargs):
             '''do something'''
             return func(*args, **kwargs)
         return wrapper
    
  • 源码,根据源码可以看出wraps 函数实现了更新包装器函数,将被包装的函数属性赋给新的包装器函数并返回,所以说该函数的作用是保留被装饰对象的属性
     # 使用偏函数,对传进来的包装器函数调用update_wrapper
     # 返回一个装饰器,该装饰器使用装饰后的函数作为包装器参数并调用wraps()的参数作为其余参数来调用update_wrapper()。 
     # 默认参数与update_wrapper()相同。
     def wraps(wrapped,
               assigned = WRAPPER_ASSIGNMENTS,
               updated = WRAPPER_UPDATES):
         """Decorator factory to apply update_wrapper() to a wrapper function
            Returns a decorator that invokes update_wrapper() with the decorated function as the wrapper argument and the arguments to wraps() as the remaining arguments. Default arguments are as for update_wrapper().
            This is a convenience function to simplify applying partial() to update_wrapper().
         """
         return partial(update_wrapper, wrapped=wrapped,
                        assigned=assigned, updated=updated)
     def update_wrapper(wrapper,
                        wrapped,
                        assigned = WRAPPER_ASSIGNMENTS,
                        updated = WRAPPER_UPDATES):
         """Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes of the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.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, {}))
         # Issue #17482: set __wrapped__ last so we don't inadvertently copy it      
         # from the wrapped function when updating __dict__
         wrapper.__wrapped__ = wrapped
         # Return the wrapper so this can be used as a decorator via partial()
         return wrapper
    
posted @ 2020-05-09 14:43  今日店休  阅读(144)  评论(0编辑  收藏  举报