Python 装饰器入门

装饰器(Decorator)

Python 的装饰器是任何可调用对象(callable object),用于修改函数(Function)或类(Class)。按照用途可分为:

  • 函数装饰器
  • 类装饰器

装饰器的接口定义可概括为:

  1. 接收某个函数或类的引用作为参数;
  2. 修改该函数或类并返回。

简单函数装饰器示例

理解装饰器

def my_decorator(func):
    def wrapped_func(arg):
        print("Before calling " + func.__name__)
        func(arg)
        print("After calling " + func.__name__)
    return wrapped_func

def foo(arg):
    print("Hi, foo has been called with " + str(arg) )

print("We call foo BEFORE decoration:")
foo("no decoration"); print()

print("We NOW decorate foo with my_decorator...\n")
foo = my_decorator(foo)

print("We call foo AFTER decoration:")
foo("decoration")

程序对应的输出为:

We call foo BEFORE decoration:
Hi, foo has been called with no decoration

We NOW decorate foo with my_decorator...

We call foo AFTER decoration:
Before calling foo
Hi, foo has been called with decoration
After calling foo

上述例子中的用法在实际开发中并不常用,我们更倾向于下面的写法。

语法糖写法

Python 提供一种更简洁、直观的装饰器写法。例如我们可以把上述例子写成更简便的形式:

def my_decorator(func):
    def wrapped_func(arg):
        print("Before calling " + func.__name__)
        func(arg)
        print("After calling " + func.__name__)
    return wrapped_func

@my_decorator
def foo(arg):
    print("Hi, foo has been called with " + str(arg) )

foo("decoration")

函数装饰器应用示例

统计函数调用次数

def call_counter(func):
    def func_with_counts(arg):
        func_with_counts.calls += 1
        return func(arg)
    func_with_counts.calls = 0
    return wrapped_func

@call_counter
def foo(arg):
    return "foo: " + str(arg)

print("foo has been called {} time(s)".format(foo.calls))
for i in range(0, 51, 10):
    foo(i)
print("foo has been called {} time(s)".format(foo.calls))

程序对应的输出结果是:

foo has been calld 0 time(s)
foo: 0
foo: 10
foo: 20
foo: 30
foo: 40
foo: 50
foo has been calld 6 time(s)

用记忆表(Memoization[1])优化 Fibonacci 数列算法

def memoize(func):
    memo = {}
    def memoized_func(arg):
        if arg not in memo:
            memo[arg] = func(arg)
        return memo[arg]
    return memoized_func

@memoize
def fib(n):
    if n == 0:
        return 0
    elif n = 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

for i in range(20):
    print(fib(i), end=', ')
print(fib(20))

程序对应的输出结果是:

	
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765

类装饰器

首先需要明确,Python 的装饰器是任何可调用对象而不仅限于函数。我们可以定义一个类并使其对象可调用。例如我们可以定义一个带缓存功能的类计算 Fibonacci:

class Fibonacci:
    def __init__(self):
        self.cache = {}
    def __call__(self, n):
        if n not in self.cache:
            if n == 0:
                self.cache[0] = 0
            elif n == 1:
                self.cache[1] = 1
            else:
                self.cache[n] = self.cache[n-1] + self.cache[n-2]
        return cache[n]

fib = Fibonacci()
for i in range(20):
    print(fib(i), end=', ')
print(fib(20))

程序对应的输出结果是:

	
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765

因此,我们可以构建一个可调用对象并令其作为装饰器。
在下面的例子中,我们用类装饰器统计函数调用次数:

class CallCounter:
    def __init__(self, func):
        self.calls = 0
        self.func = func
    def __call__(self, arg):
	self.calls += 1
        return self.func(arg)

@CallCounter
def foo(arg):
    return "foo: " + str(arg)
    

print("foo has been called {} time(s)".format(foo.calls))
for i in range(0, 51, 10):
    foo(i)
print("foo has been called {} time(s)".format(foo.calls))

Reference
Written with StackEdit.


  1. 注意 Memoization 是专业术语,不是 Memorization。 ↩︎

posted @ 2019-01-12 11:28  LexLuc  阅读(170)  评论(0编辑  收藏  举报