Python装饰器

Posted tsuko

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python装饰器相关的知识,希望对你有一定的参考价值。

Python装饰器

   一个生动的比喻是,被装饰函数是画作,而装饰器就是外面的画框。装饰器为被装饰函数提供额外的功能和行为;可以提取多个函数的共同部分作为装饰器,从而使代码更加清晰。

   学习装饰器要逐步解决的问题:

  1. 如何定义,使用装饰器?装饰器的执行顺序如何?
  2. 装饰器的接口是怎么样的?被装饰函数及其参数是如何传递的?
  3. 装饰器的高级用法:参数化装饰器和类装饰器

装饰器的基本知识

  装饰器的参数是被修饰的函数,在执行完额外工作后,要返回被装饰函数或另一个函数。

技术分享图片
# 装饰器定义
def d1(func):
    print("decorator_1")
    return func

# 放置装饰器
@d1
def func(n)
    print(n)
View Code

  上面的代码中,执行func()这个函数等价于 func =  d1( func),也就是说,执行代码时,func是被d1装饰的结果的引用,不是原始的func。f = func(100),print(f)和print(func)结果不一样。

  装饰器另外一个关键特性是:在加载模块时,他们在被装饰的函数定义之后立即执行,可见下面的代码:

技术分享图片
def decorator_1(func):
    def miao(func):
        print("fck you, return func")
        return func
    print("decorator_1, return miao")
    return miao

def decorator_2(func):
    print("decorator_2, return func")
    return func

#叠放装饰器,结果等价于d1( d2( func_1))
@decorator_1
@decorator_2
def func_1(val):
    print(val)


if __name__ == "__main__":
    print("Main()")
    func_1(1000)
View Code

  执行结果为:

decorator_2, return func
decorator_1, return miao
Main()
fuck you, return func

  这说明d2,d1两个装饰器在真正的被装饰函数func调用前,被执行。假如d2装饰了n个函数,那么d2要先执行n次。

参数传递

  通常,装饰器内部定义了函数,这个内部函数执行额外的工作。这种结构涉及“闭包”的概念。所谓闭包,就是那些可以使用非全局变量、非自身的变量的函数,例如一个函数1内部定义了函数2,函数2可以使用函数1的变量,那么函数2就是一个闭包。

技术分享图片
# 《Fluent Python》page 165 example-7-15

import time 

def clock(func):

    def clocked(*args): # ??
        # extra ..
        result =  func(*args)
        return result

    return clocked
View Code

  那么这里就有问题了:

  Q1:*args是谁的参数?

  从上下文中可知*args应该是func接受的函数,但是在上文装饰器的定义中,没有显式看到*args。由此推测:装饰器中的*args, **kwargs这些容器分别存放了被修饰函数func的positional args 和 keyword args。

  Q2:上面的假设基于di(func)的,如果改为d1(func, *args, **kwargs),那么这两个容器放的是谁的参数?

  ans:经过尝试,发现会抛出异常。如果写:

  • @d1(**dict1),                      d1()missing a required positional arg ‘func‘.
  • @d1(kwargs=dict1) ,                      d1()missing a required positional arg ‘func‘.
  • @d1(func=func_1, kwargs=dict1),   name ‘func_1‘ is not defined.

  通过查看他人代码实例,发现每个装饰器及其内部的函数,都有一个函数只有func一个参数。在《Fluent Python》page 76 example-7-25的注释中,第二条注释写道:decorate(func)是真正的函数。第一条注释:clock(...)是装饰器工厂函数。所以在Q2中,d1这个入口不是真正的装饰器,装饰器的参数只有一个func。

  下面的代码很有趣:

dict1 = {
        "name":"miao",
        "type":"zhizhi"
}

dict2 = {
    "name":"guagua",
}

def decorator_1(**kwargs):
    def miao(func):
        def zhi(**kwargs):
            for k,v in kwargs.items(): # 打印dict1
                print(k, v)
            return func
        return zhi
    for k,v in kwargs.items(): # 打印dict2
            print(k, v)
    print("decorator_1, return miao")
    return miao
#d1( d2(func_1(1000)))

@decorator_1(**dict2)
def func_1(**kwargs):
    print("fuck")


if __name__ == "__main__":
    func_1(**dict1)

 

 

  d1接受的是dict2, func_1的参数是dict1, 最终逐个打印出来的是dict1的key-value,也就是说:miao(func)是真正的装饰器,其内部函数使用的**kwargs, *args都是被装饰函数的参数,作为工厂函数的d1,它接受的参数是外部直接传给的,区别于func的参数。

  

参数化装饰器

以上是关于Python装饰器的主要内容,如果未能解决你的问题,请参考以下文章

[TimLinux] Python 装饰器

python装饰器

python装饰器关键代码

Python装饰器

python之装饰器

python 装饰器