0xe3aad

0xe3aad

Python裝飾器

引入#

當我們想在一個函數執行前後做些事情的時候,可以這樣寫:

def foo():
    print('foo()!!!')

def bar():
    print('Before')
    foo()
    print('After')

bar()

裝飾器實現#

但這樣看起來總是很別扭,我們可以這樣寫:

def foo():
    print('foo()!!!')

def bar(func):
    def inner():
        print(f'Before {func.__name__}')
        func()
        print(f'After {func.__name__}')
    return inner

bar(foo)()

在 Python 中函數是第一公民,所以可以將它作為參數

這樣我們就實現了一個簡單的裝飾器,我們可以發現這樣在調用的時候比較麻煩,Python 還提供了這樣一個語法糖:

def bar(func):
    def inner():
        print(f'In Bar Before {func.__name__}')
        func()
        print(f'In Bar After {func.__name__}')
    return inner

@bar
def foo():
    print('foo()!!!')

foo()

除此之外,Python 裝飾器可以有多個,相當於一層層的包裹:

def bar(func):
    def inner():
        print(f'In Bar Before {func.__name__}')
        func()
        print(f'In Bar After {func.__name__}')
    return inner

def baz(func):
    def inner():
        print(f'In Baz Before {func.__name__}')
        func()
        print(f'In Baz After {func.__name__}')
    return inner


@bar
@baz
def foo():
    print('foo()!!!')

foo()

上述代碼的輸出為:

In Bar Before inner
In Baz Before foo
foo()!!!
In Baz After foo
In Bar After inner

一個應用場景#

我們有一個這樣的函數:

import time

def slow_method():
    time.sleep(2)
    print('Done!')

我們想知道這個函數的運行時間,我們可以用裝飾器來實現:

import time

def timeit(func):
    def inner():
        s = time.time()
        func()
        e = time.time()
        print(f'{func.__name__} Finished in {e - s}s.')
    return inner

@timeit
def slow_method():
    time.sleep(2)
    print('Done!')

slow_method()

運行結果:

Done!
slow_method Finished in 2.0159008502960205s.

這樣我們就實現了我們的需求。

當被裝飾的函數有參數時,同樣可以:

import time

def timeit(func):
    def inner(*args):
        s = time.time()
        func(*args)
        e = time.time()
        print(f'{func.__name__} Finished in {e - s}s.')
    return inner

@timeit
def slow_method(a, b):
    time.sleep(2)
    print(f'{a} + {b} = {a+b}')
    print('Done!')

slow_method(1, 2)

運行結果

1 + 2 = 3
Done!
slow_method Finished in 2.0069477558135986s.

巧用裝飾器來實現記憶化遞歸#

首先我們有一個樸素的 Fibonacci 函數:

def fib(n):
    if n <= 1: return 1
    return fib(n - 1) + fib(n - 2)

很明顯我們知道這個函數是十分低效的,我們可以使用記憶化遞歸來優化這個函數。

cache = {}

def fib(n):
    if n in cache: return cache[n]
    if n <= 1: return 1
    cache[n] = fib(n - 1) + fib(n - 2)
    return cache[n]

print(fib(333))

這樣我們就能使 fib 函數快很多。但實現稍微有點麻煩,我們可以用裝飾器來簡化代碼:

class MyCache(object):
    def __init__(self, func):
        self.func = func
        self.cache = {}

    def __call__(self, *args):
        if args not in self.cache:
            self.cache[args] = self.func(*args)
        return self.cache[args]

@MyCache
def fib(n):
    if n <= 1: return 1
    return fib(n - 1) + fib(n - 2)


print(fib(333))

這樣我們就非常優雅地實現了 fib 函數。

參考資料#

裝飾器 Decorator - Python Weekly EP3

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。