神奇的Python装饰器

在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

Python装饰器接收一个函数,返回另一个函数。它其实是一个闭包(嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间),闭包的作用在下面的装饰器使用中可以感受到,

而返回的函数就是装饰过的函数,它可以给原函数增加一些附加功能,而不影响原函数的调用方式,简直优雅至极!

增加函数功能

假设有这么个简单的函数:

def now():
    print('Sbingo')

现在我们想在调用now时,先打印一些信息,怎么做呢?

简单,直接在now函数中打印就好了嘛!

But,这样不利于代码的复用,如果别的函数也要求打印呢?

那么我们改一下,定义另一个函数,先执行附加功能,再调用now,实现需求。:

def log(f):
    print('call %s():' %f.__name__)
    now()

调用:log(now)

输出:

call now():
Sbingo

一切看似完美,可是这样更改后,每个调用now的地方都需要改为调用log,十分麻烦。

装饰器的简单使用

这时候,就该装饰器上场了!

更改log函数:

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

    return wrapper

这里log函数就是一个装饰器,再利用语法糖@使用这个装饰器定义now函数:

@log
def now():
    print('Sbingo')

这样定义好now之后,相当于执行了:

now = log(now)

这就是语法糖@帮我们做的事。

此时,log函数接收now函数,返回了wrapper函数,并赋值给了now

所以此刻请对now函数另眼相看,这很重要。

而原先调用now函数的地方还是一样的调用方式:

now()

却得到了希望的输出:

call now():
Sbingo

一切开始变得美好~

装饰器带参数

定义带参数的装饰器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

可以看到,带参数的装饰器比之前多了1层,变成了3层嵌套

使用装饰器log定义now函数:

@log('2017')
def now():
    print('Sbingo')

这样定义好now之后,相当于执行了:

now = log('2017')(now)

再调用:now()
输出:

2017 now():
Sbingo

装饰器的调用顺序

装饰器可以叠加使用,让我们来看一下叠加后的使用顺序:

定义两个装饰器log和log_2:

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

    return wrapper

def log_2(f):
    print('enter log_2:')
    def wrapper(*args, **kw):
        print('log_2 call %s():' % f.__name__)
        return f(*args, **kw)

    return wrapper

使用装饰器log和log_2定义now函数:

@log
@log_2
def now():
    print('Sbingo')

这样定义好now之后,相当于执行了:

now = log(log_2(now))

于是,在定义时就会输出:

enter log_2:
enter log

再调用:now()
输出:

enter log_2:
enter loglog call wrapper():
log_2 call now():
Sbingo

还是很好理解的。

Python内置装饰器

Python中有3个内置装饰器,都是跟class相关的:staticmethodclassmethodproperty

  1. staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用
  2. classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)
  3. property 是属性的意思,利用它可以将类中的方法当做属性使用。

总结

理解装饰器的核心要义:
使用语法糖@定义目标函数后,目标函数已经是装饰后的函数!

参考资料:
Python装饰器学习(九步入门)
Python 装饰器

猜你喜欢

转载自blog.csdn.net/recordGrowth/article/details/78144649