Python 装饰器入门
装饰器(Decorator)
Python 的装饰器是任何可调用对象(callable object),用于修改函数(Function)或类(Class)。按照用途可分为:
- 函数装饰器
- 类装饰器
装饰器的接口定义可概括为:
- 接收某个函数或类的引用作为参数;
- 修改该函数或类并返回。
简单函数装饰器示例
理解装饰器
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.
注意 Memoization 是专业术语,不是 Memorization。 ↩︎