zoukankan      html  css  js  c++  java
  • python函数编程-装饰器decorator

    函数是个对象,并且可以赋值给一个变量,通过变量也能调用该函数:

    >>> def now():
    ...     print('2017-12-28')
    ...
    >>> l = now
    >>> l()
    2017-12-28

    利用函数的_name_属性,可以拿到函数的名字:

    >>> now.__name__
    'now'
    >>> l.__name__
    'now'

    如果我们在调用函数now()前后自动打印日志,但又不允许修改now()函数的定义——在代码运行期间动态增加功能的方式,称之为‘装饰器’Decorator。

    比如,定义一个能打印日志的decorator:

    >>> def log(func):
    ...     def wrapper(*args,**kw):
    ...         print('call %s():' % func.__name__)
    ...         return func(*args,**kw)
    ...     return wrapper
    ...

    观察log 函数,发现,本质上这就是一个返回函数的高阶函数!log作为一个decorator,接收一个函数作为参数,冰饭一个函数。借助python的@语法,把decorator置于函数的定义的地方:

    >>> @log
    ... def now():
    ...     print('2017-12-28')
    ...
    >>> now()
    call now():
    2017-12-28

    在调用now()函数时候,不仅运行了now函数,还会在此之前打印一行日志。

    其实,把@log放到now()函数的定义前,相当于执行了:

    now = log(now)

    log是一个decorator,返回一个函数,返回的这个函数名字叫wrapper,原来的now()函数还存在,这个时候now变量指向了这个返回函数wrapper。当调用now()将执行新的函数wrapper函数。wrapper函数的参数是(*args,**kw),因此wrapper()函数可以接受任意参数!在wrapper函数内部,首先打印日志,再紧接着调用原始函数。

    分割线-------------------------------

    如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数。比如要自定义log的文本,定义、用法和结果:

    >>> def log(text):
    ...     def decorator(func):
    ...         def wrapper(*args,**kw):
    ...             print('%s %s():' %(text,func.__name__))
    ...             return func(*args,**kw)
    ...         return wrapper
    ...     return decorator
    ...
    >>> @log('执行')
    ... def now():
    ...     print('2017-12-28')
    ...
    >>> now()
    执行 now():
    2017-12-28

    前面的例子中包含了两层def嵌套,后面的例子中包含了三层def嵌套。其实,三层嵌套的效果类似:

    now = log('执行')(now)

    解析:首先执行log('执行'),返回的是decorator函数,再调用返回函数,参数是now函数,最终的返回值是wrapper函数。

    但是,我们执行下面语句来测试:

    >>> now.__name__
    'wrapper'

    我们发现:经过decorator装饰后的函数,他们的__name__属性已经从now变成了wrapper。这是因为返回的那个函数wrapper函数名字就是wrapper,所以,需要把原始函数的__name__属性复制到wrapper函数中,否则,其他一些依赖函数签名的代码执行就会报错。

    实际上我们并不需要编写wrapper.__name__ = func.__name__这样的代码,python内置了functools.wraps就是为了这个。最

    最后一步,一个完整的decorator的写法如下:

    >>> import functools
    >>> def log(func):
    ...     @functools.wraps(func)
    ...     def wrapper(*args,**kw):
    ...         print('执行 %s()' %func.__name__)
    ...         return func(*args,**kw)
    ...     return wrapper
    ...
    >>> @log
    ... def now():
    ...     print('日志')
    ...
    >>> now
    <function now at 0x03317198>
    >>> now()
    执行 now()
    日志

    针对带有参数的decorator:

    >>> import functools
    >>> def log(text):
    ...     def decorator(func):
    ...         @functools.wraps(func)
    ...         def wrapper(*args,**kw):
    ...             print('%s %s()' %(text,func.__name__))
    ...             return func(*args,**kw)
    ...         return wrapper
    ...     return decorator
    ...
    >>> @log('ABC')
    ... def now():
    ...     print('这么复杂干嘛')
    ...
    >>> now()
    ABC now()
    这么复杂干嘛

    例子:设计一个decorator,可作用于任何函数上,并打印该函数的执行时间:

    >>> import time,functools
    >>> def log(func):
    ...     @functools.wraps(func)
    ...     def wrapper(*args,**kw):
    ...         t1 = time.time()
    ...         r = func(*args,**kw)
    ...         print('%s excute in %s ms'%(func.__name__,1000*(time.time()-t1)))
    ...         return r
    ...     return wrapper
    ...
    >>> @log
    ... def fast(x,y):
    ...     return x+y
    ...
    >>> @log
    ... def slow(x,y,z):
    ...     time.sleep(0.1234)
    ...     return x*y*z
    ...
    >>> @log
    ... def fast(x,y):
    ...     time.sleep(0.0012)
    ...     return x+y
    ...
    >>> fast(3,5)
    fast excute in 2.0973682403564453 ms
    8
    >>> slow(4,5,6)
    slow excute in 124.2520809173584 ms
    120
  • 相关阅读:
    李洪强经典面试题43
    李洪强经典面试题42
    李洪强经典面试题41-iOS选择题
    HTTP头部解析
    iOS
    内网安全工具之hscan扫描
    跟着百度学PHP[4]OOP面对对象编程-17-多态
    绕过注入学习笔记
    引用/别名
    跟着百度学PHP[4]OOP面对对象编程-16-switch逻辑就语句
  • 原文地址:https://www.cnblogs.com/hiwuchong/p/8136982.html
Copyright © 2011-2022 走看看