装饰器

大城市里の小女人 提交于 2020-11-08 18:18:45

一、装饰器

In [1]: def fn():
   ...:     '''this is fn'''
   ...:     

In [2]: help(fn)

Help on function fn in module __main__:

fn()
    this is fn


In [4]: fn.__doc__          # 存储函数的文档
Out[4]: 'this is fn'

In [5]: fn.__name__         # 存储函数的名字 
Out[5]: 'fn'

In [6]: dir(fn)             # 查看fn 所有的 内嵌方法
Out[6]: 
['__annotations__',
 '__call__',
 '__class__',
 '__closure__',
 '__code__',
 '__defaults__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__get__',
 '__getattribute__',
 '__globals__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__kwdefaults__',
 '__le__',
 '__lt__',
 '__module__',
 '__name__',
 '__ne__',
 '__new__',
 '__qualname__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']

In [7]: 
In [2]: def logger(fn):
   ...:     def wrap(*args, **kwargs):
   ...:         start = datetime.datetime.now()
   ...:         ret = fn(*args, **kwargs)
   ...:         end = datetime.datetime.now()
   ...:         print('{} called took {}'.format(fn.__name__, end - star
   ...: t))
   ...:         return ret
   ...:     copy_propertities(fn, wrap)
   ...:     return wrap
   ...: 

In [3]: def copy_propertities(src, dst):
   ...:     dst.__name__ = src.__name__
   ...:     dst.__doc__ = src.__doc__
   ...:     

In [4]: @logger
   ...: def sleep(x):
   ...:     '''this is sleep'''
   ...:     time.sleep(x)
   ...:     

In [5]: import time, datetime

In [6]: sleep.__doc__
Out[6]: 'this is sleep'

In [7]: sleep.__name__
Out[7]: 'sleep'

In [8]: 

In [8]: def copy_propertities(src): # 柯里化
   ...:     def _copy(dst):
   ...:         dst.__name__ = src.__name__
   ...:         dst.__doc__ = src.__doc__
   ...:     return _copy
   ...: 

In [9]: def logger(fn):
   ...:     def wrap(*args, **kwargs):
   ...:         start = datetime.datetime.now()
   ...:         ret = fn(*args, **kwargs)
   ...:         end = datetime.datetime.now()
   ...:         print('{} called took {}'.format(fn.__nam
   ...: e__, end - start))
   ...:         return ret
   ...:     copy_propertities(fn)(wrap)
   ...:     return wrap
   ...: 

In [10]: @logger
    ...: def sleep(x):
    ...:     '''this is sleep'''
    ...:     time.sleep(x)
    ...:     

In [11]: sleep.__doc__
Out[11]: 'this is sleep'

In [12]: sleep.__name__
Out[12]: 'sleep'

In [13]: def copy_propertities(src): # 柯里化,带参数的装饰器
    ...:     def _copy(dst):
    ...:         dst.__name__ = src.__name__
    ...:         dst.__doc__ = src.__doc__
    ...:         return dst
    ...:     return _copy
    ...: 

In [14]: 
In [14]: def logger(fn):
    ...:     @copy_propertities(fn)
    ...:     def wrap(*args, **kwargs):
    ...:         start = datetime.datetime.now()
    ...:         ret = fn(*args, **kwargs)
    ...:         end = datetime.datetime.now()
    ...:         print('{} called took {}'.format(fn.__na
    ...: me__, end - start))
    ...:         return ret
    ...:     return wrap
    ...: 

In [15]: @logger
    ...: def sleep(x):
    ...:     '''this is sleep'''
    ...:     time.sleep(x)
    ...:     

In [16]: sleep.__doc__
Out[16]: 'this is sleep'

In [17]: sleep.__name__
Out[17]: 'sleep'

In [18]: 

  1. AOP: 面向切面编程, 为了解决一类问题

In [19]: import functools

In [20]: help(functools.wraps)



Help on function wraps in module functools:

wraps(wrapped, assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'), updated=('__dict__',))
    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().
(END)
In [21]: def logger(fn):
    ...:     @functools.wraps(fn)
    ...:     def wrap(*args, **kwargs):
    ...:         start = datetime.datetime.now()
    ...:         ret = fn(*args, **kwargs)
    ...:         end = datetime.datetime.now()
    ...:         print('{} called took {}'.format(fn.__na
    ...: me__, end - start))
    ...:         return ret
    ...:     return wrap
    ...: 

In [22]: @logger(2)
    ...: def sleep(x):
    ...:     '''this is sleep'''
    ...:     time.sleep(x)
    ...:     
---------------------------------------------------------
TypeError               Traceback (most recent call last)
<ipython-input-22-26fda312a82d> in <module>()
----> 1 @logger(2)
      2 def sleep(x):
      3     '''this is sleep'''
      4     time.sleep(x)
      5 

<ipython-input-21-5b4e194e9175> in wrap(*args, **kwargs)
      3     def wrap(*args, **kwargs):
      4         start = datetime.datetime.now()
----> 5         ret = fn(*args, **kwargs)
      6         end = datetime.datetime.now()
      7         print('{} called took {}'.format(fn.__name__, end - start))

TypeError: 'int' object is not callable

In [23]: sleep.__name__
Out[23]: 'sleep'

In [24]: sleep.__doc__
Out[24]: 'this is sleep'

In [25]: start = datetime.datetime.now()

In [26]: end = datetime.datetime.now()

In [27]: delta = end - start

In [28]: delta.seconds
Out[28]: 9

In [29]: 
In [29]: def logger(s):
    ...:     def _logger(fn):
    ...:         @functools.wraps(fn)
    ...:         def wrap(*args, **kwargs):
    ...:             start = datetime.datetime.now()
    ...:             ret = fn(*args, **kwargs)
    ...:             end = datetime.datetime.now()
    ...:             if (end - start).seconds > s:
    ...:                 print('{} called took {}'.format
    ...: (fn.__name__, end - start))
    ...:             return ret
    ...:         return wrap
    ...:     return _logger
    ...: 

In [30]: @logger(2)
    ...: def sleep(x):
    ...:     time.sleep(x)
    ...:     

In [31]: sleep(3)
sleep called took 0:00:03.003120

In [32]: sleep(1)

In [33]: logger(2)(sleep)(3)
sleep called took 0:00:03.003038
sleep called took 0:00:03.003127

In [34]: 
In [34]: def logger(s, p=lambda name, t: print('call {} t
    ...: ook {}'.format(name, t))):
    ...:     def _logger(fn):
    ...:         @functools.wraps(fn)
    ...:         def wrap(*args, **kwargs):
    ...:             start = datetime.datetime.now()
    ...:             ret = fn(*args, **kwargs)
    ...:             end = datetime.datetime.now()
    ...:             if (end - start).seconds > s:
    ...:                 p(fn.__name__, end - start)
    ...:             return ret
    ...:         return wrap
    ...:     return _logger
    ...: 

In [35]: @logger(2)
    ...: def sleep(x):
    ...:     time.sleep(x)
    ...:     

In [36]: sleep(1)

In [37]: sleep(3)
call sleep took 0:00:03.003093

In [38]: 

所以,装饰器的作用就是 在不改变函数本身的情况下,做一些预定义的逻辑处理

In [38]: @logger(2, p=lambda name, t: None)
    ...: def sleep(x):
    ...:     time.sleep(x)
    ...:     

In [39]: sleep(1)

In [40]: sleep(3)

In [41]: def logger(s):
    ...:     def _logger(p=lambda name, t: print('call {}
    ...:  took {}'.format(name, t))):
    ...:         def __logger(fn):
    ...:             @functools.wraps(fn)
    ...:             def wrap(*args, **kwargs):
    ...:                 start = datetime.datetime.now()
    ...:                 ret = fn(*args, **kwargs)
    ...:                 end = datetime.datetime.now()
    ...:                 if (end - start).seconds > s:
    ...:                     p(fn.__name__, end - start)
    ...:                 return ret
    ...:             return wrap
    ...:         return __logger
    ...:     return _logger
    ...: 

In [42]: @logger(2)()
    ...: def sleep(x):
    ...:     time.sleep(x)
  File "<ipython-input-42-c5a11f3c0ec8>", line 1
    @logger(2)()
              ^
SyntaxError: invalid syntax


In [43]: logger(2)()(sleep)(3)
call sleep took 0:00:03.003091

In [44]: lg = logger(2)

In [45]: lg
Out[45]: <function __main__.logger.<locals>._logger>

In [46]: @lg()
    ...: def sleep(x):
    ...:     time.sleep(x)
    ...:     

In [47]: sleep(3)
call sleep took 0:00:03.002025

In [48]: 

二、类型注解

In [1]: a = 1

In [2]: type(a)
Out[2]: int

In [3]: a = 's'

In [4]: def add(x, y):
   ...:     return x + y
   ...: 

In [5]: add(1, 3)
Out[5]: 4

In [6]: add('a', 'b')
Out[6]: 'ab'

In [7]: add(1, 'a')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-001bda6fec17> in <module>()
----> 1 add(1, 'a')

<ipython-input-4-5f28f1822fab> in add(x, y)
      1 def add(x, y):
----> 2     return x + y

TypeError: unsupported operand type(s) for +: 'int' and 'str'

In [8]: def fn():
   ...:     '''
   ...:     总是返回True
   ...:     return True
   ...:     '''
   ...:     return False
   ...: 

In [9]: def add(x: int, y: int) -> int:
   ...:     return x + y
   ...: 

In [10]: 
  • 冒号后面跟 参数类型
  • 箭头后面跟 返回值类型
In [10]: add.__annotations__
Out[10]: {'return': int, 'x': int, 'y': int}

In [11]: import typing

In [12]: def _sum(lst: typing.List[int]) -> int:
    ...:     ret = 0
    ...:     for x in lst:
    ...:         ret += x
    ...:     return ret
    ...:     

In [13]: _sum.__annotations__
Out[13]: {'lst': typing.List[int], 'return': int}

In [14]: import inspect

In [16]: sig = inspect.signature(add)

In [17]: list(sig.parameters.values())[0].annotation
Out[17]: int

In [18]: sig.parameters['x'].annotation
Out[18]: int

In [19]: sig
Out[19]: <Signature (x:int, y:int) -> int>

In [20]: sig.parameters['x']
Out[20]: <Parameter "x:int">

In [21]: sig.parameters['x'].annotation
Out[21]: int

In [22]: sig.parameters.values()
Out[22]: odict_values([<Parameter "x:int">, <Parameter "y:int">])

In [23]: sig.parameters['x'].name
Out[23]: 'x'
In [28]: @type   # 框架

    ...: def add(x: int, y: int) -> int:
    ...:     return x + y

    ...: add(1, 'a')  # raise,print
    ...:  
In [8]: import functools, inspect

In [9]: def typed(fn):
   ...:     @functools.wraps(fn)
   ...:     def wrap(*args, **kwargs):
   ...:         params = inspect.signature(fn).parameters
   ...:         
   ...:     # kwargs 检查
   ...:         for k, v in kwargs.items():
   ...:             if not isinstance(v, params[k].annotation):
   ...:                 raise TypeError('parameter {} require {}, but {}'.format(k, params[k].annot
   ...: ation, type(k)))
   ...:    
   ...: 
   ...:     # args 检查
   ...:         for i, arg in enumerate(args):
   ...:             param = list(params.values())[i]
   ...:             if not isinstance(arg, param.annotation):
   ...:                 raise TypeError('paramter {} require {}, but {}'.format(param.name, param.a
   ...: nnotation, type(arg)))
   ...:         return fn(*args, **kwargs)
   ...:     return wrap
   ...:                       

In [10]: @typed
    ...: def add(x: int, y: int) -> int:
    ...:     return x + y
    ...: 

In [11]: add(3, 2)
Out[11]: 5

In [12]: add(3, 'a')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-39bc36d291f5> in <module>()
----> 1 add(3, 'a')

<ipython-input-9-6908a9ee8192> in wrap(*args, **kwargs)
     14             param = list(params.values())[i]
     15             if not isinstance(arg, param.annotation):
---> 16                 raise TypeError('paramter {} require {}, but {}'.format(param.name, param.annotation, type(arg)))
     17         return fn(*args, **kwargs)
     18     return wrap

TypeError: paramter y require <class 'int'>, but <class 'str'>

In [13]: add(y=3, x='a')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-6d2bbb3e8895> in <module>()
----> 1 add(y=3, x='a')

<ipython-input-9-6908a9ee8192> in wrap(*args, **kwargs)
      7         for k, v in kwargs.items():
      8             if not isinstance(v, params[k].annotation):
----> 9                 raise TypeError('parameter {} require {}, but {}'.format(k, params[k].annotation, type(k)))
     10 
     11 

TypeError: parameter x require <class 'int'>, but <class 'str'>

In [14]: 



In [15]: @typed
    ...: def add(x: int, y) -> int:
    ...:     return x + y
    ...: 

In [16]: add(1, 1) # 上面的代码,漏掉了如果参数的类型没有提供的情况。下面进行改进
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-16-d6a28b0c7f0b> in <module>()
----> 1 add(1, 1)

<ipython-input-9-6908a9ee8192> in wrap(*args, **kwargs)
     14             param = list(params.values())[i]
     15             if not isinstance(arg, param.annotation):
---> 16                 raise TypeError('paramter {} require {}, but {}'.format(param.name, param.annotation, type(arg)))
     17         return fn(*args, **kwargs)
     18     return wrap

TypeError: paramter y require <class 'inspect._empty'>, but <class 'int'>

In [17]: 
# 上面的代码,漏掉了如果参数的类型没有提供的情况。下面进行改进
# 类型检查(改进后)
In [23]: import functools, inspect

In [24]: def typed(fn):
    ...:     @functools.wraps(fn)
    ...:     def wrap(*args, **kwargs):
    ...:         params = inspect.signature(fn).parameters
    ...:        
    ...: 
    ...:         # kwargs 检查
    ...:         for k, v in kwargs.items():
    ...:             param = params[k]
    ...:             if param.annotation != inspect._empty and not isinstance(v, param.annotation):
    ...: 
    ...:                 raise TypeError('parameter {} require {}, but {}'.format(k, params[k].anno
    ...: tation, type(k)))
    ...:    
    ...:         # args 检查
    ...:         for i, arg in enumerate(args):
    ...:             param = list(params.values())[i]
    ...:             if param.annotation != inspect._empty and not isinstance(arg, param.annotation
    ...: ):
    ...:                 raise TypeError('parameter {} require {}, but {}'.format(param.name, param
    ...: .annotation, type(arg)))
    ...:         return fn(*args, **kwargs)
    ...:     return wrap
    ...:                 

In [25]: @typed
    ...: def add(x: int, y: int) -> int:
    ...:     return x + y
    ...: 

In [26]: add(3, 5)
Out[26]: 8

In [27]: add(3, 'a')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-27-39bc36d291f5> in <module>()
----> 1 add(3, 'a')

<ipython-input-24-22f251518b19> in wrap(*args, **kwargs)
     15             param = list(params.values())[i]
     16             if param.annotation != inspect._empty and not isinstance(arg, param.annotation):
---> 17                 raise TypeError('parameter {} require {}, but {}'.format(param.name, param.annotation, type(arg)))
     18         return fn(*args, **kwargs)
     19     return wrap

TypeError: parameter y require <class 'int'>, but <class 'str'>

In [28]: @typed
    ...: def add(x: int, y) -> int:
    ...:     return x + y
    ...: 

In [29]: add(3, 5)
Out[29]: 8

In [30]: 

接受一个函数参数,然后把这个函数的某些参数变成默认参数,并返回一个新函数

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!