此係列文件:
二、裝飾器的高階用法
將引數傳遞給裝飾函式
#它不是黑魔法,只是給包裝(wrapper)傳遞引數:
def a_decorator_passing_arguments(function_to_decorate):
def a_wrapper_accepting_arguments(arg1, arg2):
print("I got args! Look: {0}, {1}".format(arg1, arg2))
function_to_decorate(arg1, arg2)
return a_wrapper_accepting_arguments
#因為當您呼叫裝飾器返回的函式時,呼叫的包裝器(wrapper),將引數傳遞給被包裝器包裝的函式
@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
print("My name is {0} {1}".format(first_name, last_name))
print_full_name("Peter", "Venkman")
# 輸出:
#I got args! Look: Peter Venkman
#My name is Peter Venkman
將引數傳遞給裝飾器
關於將引數傳遞給裝飾器本身,您怎麼認為?
因為裝飾器必須接受一個函式作為引數,所以這可能會有些彆扭。
因此,您不能將裝飾函式的引數直接傳遞給裝飾器。
在尋求解決方案之前,讓我們寫一些提醒:
#裝飾器是普通函式
def my_decorator(func):
print("I am an ordinary function")
def wrapper():
print("I am function returned by the decorator")
func()
return wrapper
# 因此,你可以呼叫它,而不用 "@"
def lazy_function():
print("zzzzzzzz")
decorated_function = my_decorator(lazy_function)
#輸出: I am an ordinary function
# 它輸出了 "I am an ordinary function", 因為你只是呼叫了裝飾器,而沒有呼叫函式:
# 這裡沒有什麼神奇的地方,使用'@'
@my_decorator
def lazy_function():
print("zzzzzzzz")
#outputs: I am an ordinary function
結果一樣。my_decorator
”被呼叫了。
因此,當您使用時@my_decorator
,您要告訴Python,通過變數來呼叫my_decorator
標記了的函式。
def decorator_maker():
print("I make decorators! I am executed only once: "
"when you make me create a decorator.")
def my_decorator(func):
print("I am a decorator! I am executed only when you decorate a function.")
def wrapped():
print("I am the wrapper around the decorated function. "
"I am called when you call the decorated function. "
"As the wrapper, I return the RESULT of the decorated function.")
return func()
print("As the decorator, I return the wrapped function.")
return wrapped
print("As a decorator maker, I return a decorator")
return my_decorator
#讓我們新建一個裝飾器
new_decorator = decorator_maker()
#輸出:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
# 讓我們裝飾這個函式
def decorated_function():
print("I am the decorated function.")
decorated_function = new_decorator(decorated_function)
#輸出:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function
# 讓我們呼叫這個函式
decorated_function()
#輸出:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
毫不奇怪,跟我們前邊演示的內容一樣。
讓我們再做一次完全一樣的事情,但是這次我們跳過所有討厭的中間變數:
def decorated_function():
print("I am the decorated function.")
decorated_function = decorator_maker()(decorated_function)
#輸出:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.
# Finally:
decorated_function()
#輸出:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
讓我們把它變的更精簡:
@decorator_maker()
def decorated_function():
print("I am the decorated function.")
#輸出:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.
#最終:
decorated_function()
#輸出:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
嘿,你看到了嗎?我們使用了帶有“ @
”語法的函式呼叫!
因此,回到帶有引數的裝飾器。
如果我們可以使用函式即時生成裝飾器,則可以將引數傳遞給該函式,對嗎?
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
def my_decorator(func):
#這裡傳遞的引數是閉包的。
#如果您對封包感到不舒服,可以忽略這點。
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
#不要混淆裝飾器引數和函式引數!
def wrapped(function_arg1, function_arg2) :
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
function_arg1, function_arg2))
return func(function_arg1, function_arg2)
return wrapped
return my_decorator
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))
decorated_function_with_arguments("Rajesh", "Howard")
#輸出:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Sheldon
# - from the function call: Rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard
記住它:帶引數的裝飾器,可以將變數作為引數:
c1 = "Penny"
c2 = "Leslie"
@decorator_maker_with_arguments("Leonard", c1)
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments:"
" {0} {1}".format(function_arg1, function_arg2))
decorated_function_with_arguments(c2, "Howard")
#輸出:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Penny
# - from the function call: Leslie Howard
#Then I can pass them to the decorated function
#I am the decorated function and only know about my arguments: Leslie Howard
如您所見,您可以像任何函式傳遞引數一樣傳遞引數給裝飾器。
您甚至可以根據需要使用*args, **kwargs
。
但是請記住,裝飾器僅被呼叫一次,僅在Python匯入指令碼時。之後,您將無法動態設定引數。
當您執行“ import x”時,該函式已經被修飾,因此您無法進行任何更改。
本文首發於BigYoung小站