Python 進階用法 (持續更新)
阿新 • • 發佈:2019-01-12
裝飾器(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)
用記憶表(Memoization1)優化 Fibonacci 數列演算法
def memoize(func):
memo = {}
def memoized_func(arg):
if arg not in memo:
memo[arg] = func(arg)
return memo
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。↩