Python Decorator

 Published On February 01, 2015

Decorator, 装饰器。Decorator的用途在于允许在函数和类中嵌入或者修改代码。

classmethod and staticmethod

staticmethod(function)

这将返回一个function的静态方法。把类的一个方法的声明为静态方法,具体用法如下:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

或者

class C:
    def f(arg1, arg2, ...): ...
    f = staticmethod(f)

类的静态方法既可以被类调用也可以被类的实例调用,例如:

C.f(...)

或者:

C(...).f(...)

注意,staticmethod 并不将类自身或其示例接收为隐含的第一个参数,在这一点上staticmethod 和 classmethod 存在区别。

classmethod(function)

这将返回function的一个类方法。具体用法如下:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ...

或者:

class C:
    def f(cls, arg1, arg2, ...): ...
    f = classmethod(f)

类的classmethod既可以被类调用也可以被类的实例调用,例如:

C.f(...)

或者:

C(...).f(...)

注意,classmethod 像将类的示例函数一样,将类自身接收为隐含的第一个参数。

Python Decorators for Functions and Methods

函数decorator

一个函数decorators用于函数定义,它位于在函数定义之前的一行。例如:

@decoFunc
def aFunc():
    print('a Func is called')

当编译器经过这段代码时,aFunc()被编译然后将结果函数对象传递给decoFunc代码,后者创建一个类函数对象并取代原来的aFunc()。根据这一说法,那么:

@A
@B
@C
def func(arg1, arg2, ...): ...

等价于

def func(arg1, arg2, ...): ...
func = A(B(C(func)))

例如:

def decFunc(f):
    def inner(f):
        return 100
    return inner

@decFunc
def aFunc(x):
    print('value x is %d'%(x))
    print('aFunc called')

print(aFunc(10))

程序的输出结果为

100

并没有输出

aFunc called

也就是说,在装饰器函数中,函数aFunc的行为被改变了。

类decorator

与函数decorator类似,也可以将类作为decorator,只要实现__call__方法即可。

一个类如果实现了__call__方法,那么这个类的对象便是可调用的。例如:

class decCls():
    def __call__():
        print('__call__ of decCls called.')

decCls()()

运行,输出:

__call__ of decCls called.

下来举一个将类作为decorator的例子:

class decCls():
    def __init__(self, f):
        self.f = f
    def __call__(self, x):
        print('decorator called')
        return 10000

@decCls
def aFunc(x):
    print('value x is %d'%(x))
    print('aFunc called')

print(aFunc(10))

此处就相当于

def aFunc(x):
    print('value x is %d'%(x))
    print('aFunc called')
aFunc = decCls(aFunc)

那么在调用aFunc的时候,其实是在调用decCls(aFunc),也就是运行的是

decCls(aFunc)(10)

最后相当于调用的是装饰器类decCls__call__方法。

decorator的用途

decorator的一个很大的用途在于可以“掌控”一个函数的运行。看下面这段代码:

def fn_decorator(fn):
    def wrapper(*args):
        print('call wrapper')
        return fn(*args)
    return wrapper

@fn_decorator
def fn(...):
    # ...

在这儿,调用fn时其实是在调用wrapper,因此,wrapper是可以控制fn是否运行的。

Decorator带参数

之前的Decorator都是没有参数的,那么,有参数的Decorator又是如何工作的呢?

如果是单个带参数的Decorator,那么:

@dec(param)
def aFunc():
    #...

等价于

def aFunc():
aFunc = dec(param)(aFunc)

如果是多重Decorator,并且带参数,那么有:

@dec_a(params1)
@dec_b(params2)
@dec_c(params3)
def method(args):
    pass

等价于

def method(args):
    pass
method = dec_a(params1)(dec_b(params2)(dec_c(params)(method))) 

可见,Decorator还是很灵活很强大的。

应用举例

定义一个装饰器(Decorator)来测量函数的执行时间:

import time

def fn_timer(f):
    def wrapper(*args):
        t0 = time.time()
        ans = f(*args)
        t1 = time.time()
        print('f start at %f, end at %f, running %f'%(t0,t1,t1-t0))
        return ans
    return wrapper

使用这个装饰器的例子:

@fn_timer
def sum_fn(n):
    ans = 0
    for i in range(0, n):
        ans += i
    return ans

print(sum_fn(10000000))

这样,便可以在不改变sum_fn的接口和内部实现的前提下实现新的功能。

参考

  1. PEP 318

Tags: Python



Comments:

comments powered by Disqus