Python進階: Decorator 裝飾器你太美

函數 -> 裝飾器

  函數的4個核心概念

  1.函數能夠賦與變量緩存

def func(message):
    print('Got a message: {}'.format(message))

send_message = func
send_message('hello world')
#輸出
#Got a message: hello world

  2.函數能夠看成函數的參數閉包

def get_message(message):
    return 'Got a message: ' + message

def root_call(func, message):
    print(func(message))

root_call(get_message, 'hello world')
輸出
#Got a message: hello world

  3.函數裏嵌套函數app

def func(message):
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message(message)

func('hello world')
輸出
#Got a message: hello world

  4.函數做爲函數返回值(閉包)函數

def func_closure():
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message

send_message = func_closure()
send_message('hello world')
#輸出
#Got a message: hello world
 

簡單裝飾器

  例post

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

def greet():
    print('hello world')

greet = my_decorator(greet)
greet()

  使用語法糖 @測試

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

@my_decorator
def greet():
    print('hello world')

greet()
# 輸出
# wrapper of decorator
# hello world

帶有參數的裝飾器

  直接在 wrapper函數中加上參數
def my_decorator(func):
    def wrapper(message):
        print('wrapper of decorator')
        func(message)
    return wrapper


@my_decorator #至關於 greet == wrapper(message)
def greet(message):
    print(message)

greet('hello world')
# 輸出
#wrapper of decorator
#hello world

  這個裝飾器只能用在有一個參數的函數,若是想對任意參數的函數通用,能夠這麼寫spa

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper

帶自定義參數的裝飾器

  利用裝飾器自定義參數這特性,實現重複執行裝飾器內部函數
def repeat(num):
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator


@repeat(4)
def greet(message):
    print(message)

greet('hello world')

# 輸出:
# wrapper of decorator
# hello world
# wrapper of decorator
# hello world
# wrapper of decorator
# hello world
# wrapper of decorator
# hello world

原函數仍是原函數?

greet.__name__
#輸出
'wrapper'

help(greet)
# 輸出
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

  能夠看出,原函數的原信息會被wrapper取代日誌

  若是不想其改變,那麼可用內置裝飾器@functools.wraps將原函數的元信息拷貝過去。
import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper

@my_decorator
def greet(message):
    print(message)

greet.__name__

# 輸出
#'greet'

 類裝飾器

  類裝飾器主要依賴於 __call__()函數,每當調用類實例時,__call__()函數會被執行一次
class Count:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print('num of calls is: {}'.format(self.num_calls))
        return self.func(*args, **kwargs)

@Count
def example():
    print("hello world")

example()

# 輸出
# num of calls is: 1
# hello world

example()

# 輸出
# num of calls is: 2
# hello world

裝飾器的嵌套

@decorator1
@decorator2
@decorator3
def func():
    ...
#至關於 decorator1(decorator2(decorator3(func)))
import functools

def my_decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator1')
        func(*args, **kwargs)
    return wrapper


def my_decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator2')
        func(*args, **kwargs)
    return wrapper


@my_decorator1
@my_decorator2
def greet(message):
    print(message)


greet('hello world')

# 輸出
# execute decorator1
# execute decorator2
# hello world

裝飾器的實例用法

  1)身份驗證,不登陸不容許操做code

import functools

def authenticate(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        request = args[0]
        if check_user_logged_in(request): # 若是用戶處於登陸狀態
            return func(*args, **kwargs) # 執行函數 post_comment()
        else:
            raise Exception('Authentication failed')
    return wrapper

@authenticate
def post_comment(request, ...)
    ...

  2)日誌記錄 可測試函數的執行時間orm

import time
import functools

def log_execution_time(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
        return res
    return wrapper

@log_execution_time
def calculate_similarity(items):
    ...

  3) 合法性檢測

import functools

def validation_check(input):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        ... # 檢查輸入是否合法

@validation_check
def neural_network_training(param1, param2, ...):
    ...

LRU cache. @lru_cache緩存裝飾器
@lru_cache
def check(param1, param2, ...) # 檢查用戶設備類型,版本號等等
    ...

參考

  極客時間《Python核心技術與實戰》專欄

相關文章
相關標籤/搜索