Skip to content

Python中常用的装饰器

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
@log
def now():
    print('2015-3-25')
>>> now()
call now():
2015-3-25

1 @property

修饰方法,使方法作为属性使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> class example(object):
...     @property
...     def example_method(self):
...         return 123
... 
>>> test=example()
>>> test.example_method
123
>>> test.example_method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

2 @\<attr>.setter:用于将一个普通的函数标记为属性的修改器方法(即 setter 方法)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class MyClass:
    def __init__(self, value):
        self._value = value

    @classmethod
    def class_method(cls):
        # 在类方法中访问类变量
        print(cls.class_variable)

    @staticmethod
    def static_method():
        # 在静态方法中不能访问类或实例变量
        print("This is a static method")

    @property
    def value(self):
        # 访问器方法,获取值
        return self._value

    @value.setter
    def value(self, value):
        # 修改器方法,设置值
        self._value = value

obj = MyClass(5)
obj.value = 10  # 调用修改器方法
print(obj.value)  # 调用访问器方法

3@abstractmethod

抽象方法表示基类的一个方法,没有实现,所以基类不能实例化,子类实现了该抽象方法才能被实例化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from abc import ABCMeta, abstractmethod
class Backbone(nn.Module, metaclass=ABCMeta):
    def __init__(self):
        super().__init__()

    @abstractmethod
    def forward(self):
        pass

    @property
    def size_divisibility(self):
        return 0

Python的abc提供了@abstractmethod装饰器实现抽象方法,下面以Python3的abc模块举例。 @abstractmethod: 见下图的代码,基类Foo的fun方法被@abstractmethod装饰了,所以Foo不能被实例化;子类SubA没有实现基类的fun方法也不能被实例化;子类SubB实现了基类的抽象方法fun所以能实例化。

完整代码: 在Python3.4中,声明抽象基类最简单的方式是子类话abc.ABC;Python3.0到Python3.3,必须在class语句中使用metaclass=ABCMeta;Python2中使用__metaclass__=ABCMeta

Python3.4 实现方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from abc import ABC, abstractmethod
class Foo(ABC):
    @abstractmethod
    def fun(self):
        '''please Implemente in subclass'''


class SubFoo(Foo):
    def fun(self):
        print('fun in SubFoo')

a = SubFoo()
a.fun()

Python3.0到Python3.3的实现方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from abc import abstractmethod, ABCMeta

class Bar(metaclass=ABCMeta):
    @abstractmethod
    def fun(self):
        '''please Implemente in subclass'''

class SubBar(Bar):
    def fun(self):
        print('fun in SubBar')
b = SubBar()
b.fun()

4 @classmethod @staticmethoed

@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义

@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
>>> class Demo:
...    @classmethod
...    def klassmeth(*args):
...        return args
...    @staticmethod
...    def statmeth(*args):
...        return args
>>> Demo.klassmeth() # 无论是否传递参数第一个参数都是class本身
(<class '__main__.Demo'>,) 
>>> Demo.statmeth()
()
>>> Demo.klassmeth('apple') # 无论是否传递参数第一个参数都是class本身
(<class '__main__.Demo'>, 'apple')
>>> Demo.statmeth('apple')
('apple',)

example,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

5 @lru_cache 基于缓存能够加速代码性能:尤其是递归结构:

1
2
3
@lru_cache
def factorial(n):
    return n * factorial(n-1) if n else 1

6 @jit jit全称“Just In Time compilation”,也能加速代码运行速度,但是通常是对于数据量大的计算才能显示优势,简单的计算可能还会变慢:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from numba import jit
import random

@jit(nopython=True)
def monte_carlo_pi(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples