我終於弄懂了Python的裝飾器(二)

此係列文檔:python

1. 我終於弄懂了Python的裝飾器(一)閉包

2. 我終於弄懂了Python的裝飾器(二)app

3. 我終於弄懂了Python的裝飾器(三)函數

4. 我終於弄懂了Python的裝飾器(四)post

2、裝飾器的高級用法

將參數傳遞給裝飾函數

#它不是黑魔法,只是給包裝(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

將參數傳遞給裝飾器

關於將參數傳遞給裝飾器自己,您怎麼認爲?code

由於裝飾器必須接受一個函數做爲參數,因此這可能會有些彆扭。orm

所以,您不能將裝飾函數的參數直接傳遞給裝飾器。文檔

在尋求解決方案以前,讓咱們寫一些提醒:get

#裝飾器是普通函數
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標記了的函數。it

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小站
相關文章
相關標籤/搜索