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

此係列文檔:python

1. 我終於弄懂了Python的裝飾器(一)設計模式

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

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

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

1、裝飾器基礎(什麼是裝飾器)

Python的函數是對象

要了解裝飾器,您必須首先了解函數是Python中的對象。這具備重要的聯繫。post

讓咱們來看一個簡單的例子:spa

def shout(word="yes"):
    return word.capitalize()+"!"

print(shout())
# 輸出 : 'Yes!'

# 做爲一個對象,您能夠像其餘對象同樣將函數賦給變量
scream = shout

#注意咱們不使用括號:咱們沒有調用函數
#咱們將函數「shout」放入變量「scream」。
#這意味着您能夠從「scream」中調用「shout」:

print(scream())
# 輸出: 'Yes!'

#除此以外,這意味着您能夠刪除舊名稱'shout',該功能仍可從'scream'訪問

del shout

try:
    print(shout())
except NameError as e:
    print(e)
    #輸出: "name 'shout' is not defined"

print(scream())
# 輸出: 'Yes!'複製代碼

請記住這一點,咱們將在不久後回頭再說。設計

Python函數的另外一個有趣特性是能夠在另外一個函數中定義它們!code

def talk():

    # 您能夠在「talk」中動態定義一個函數...
    def whisper(word="yes"):
        return word.lower()+"..."

    # ...而且能夠立馬使用它。
    print(whisper())

#您每次調用「talk」,都會定義「whisper」,而後在「talk」中調用「whisper」。

talk()
# 輸出: 
# "yes..."

# 可是"whisper"不存在"talk"定義之外的地方:

try:
    print(whisper())
except NameError as e:
    print(e)
    #輸出 : "name 'whisper' is not defined"複製代碼

函數參考

OK,應該還在看吧?如今開始有趣的部分...對象

您已經看到函數是對象。 所以,函數:

  • 能夠分配給變量
  • 能夠在另外一個函數中定義

這意味着一個函數能夠return另外一個功能

def getTalk(kind="shout"):

    # 咱們頂一個即時的函數
    def shout(word="yes"):
        return word.capitalize()+"!"

    def whisper(word="yes") :
        return word.lower()+"...";

    # 而後咱們返回它
    if kind == "shout":
        #咱們不使用「()」,因此咱們沒有調用函數,咱們正在返回這個函數對象
        return shout  
    else:
        return whisper

#獲取函數並將其分配給變量: "talk"
talk = getTalk()      

#您能夠看到「talk」是一個函數對象:
print(talk)
#輸出 : <function shout at 0xb7ea817c>

#函數對象返回的內容:
print(talk())
#輸出 : Yes!

#若是您感到困惑,甚至能夠直接使用它:
print(getTalk("whisper")())
#outputs : yes...複製代碼

還有更多的內容!

若是能夠return一個函數,則能夠將其中一個做爲參數傳遞:

def doSomethingBefore(func): 
    print("I do something before then I call the function you gave me")
    print(func())

doSomethingBefore(scream)
#輸出: 
#I do something before then I call the function you gave me
#Yes!複製代碼

好吧,您只具有了解裝飾器所需的全部信息。 您會看到,裝飾器是「包裝器(wrappers)」,這意味着它們使您能夠在裝飾函數以前和以後執行代碼,而無需修改函數自己的代碼內容。

手工進行裝飾

您將知道如何進行手動操做:

#裝飾器是講另一個函數做爲參數的函數
def my_shiny_new_decorator(a_function_to_decorate):

    # 在內部,裝飾器動態定義一個函數:包裝器(wrappers)。
    # 此功能將被包裝在原始功能的外部,以便它能夠在代碼以前和以後執行代碼。
    def the_wrapper_around_the_original_function():

        # 在調用原始函數以前,將要執行的代碼放在此處
        print("Before the function runs")

        #在此處調用函數(使用括號)
        a_function_to_decorate()

        # 在調用原始函數後,將要執行的代碼放在此處
        print("After the function runs")

    #至此,「a_function_to_decorate」從未執行過。
    #咱們返回剛剛建立的包裝函數。
    #包裝器包含函數和在代碼以前和以後執行的代碼。隨時可使用!
    return the_wrapper_around_the_original_function

#如今,假設您建立了函數,可是不想再修改的函數。
def a_stand_alone_function():
    print("I am a stand alone function, don't you dare modify me")

a_stand_alone_function() 
#輸出: I am a stand alone function, don't you dare modify me

#因此,您能夠裝飾它以擴展其行爲。
#只需將其傳遞給裝飾器,它將動態地包裝在
#您想要的任何代碼中,併爲您返回準備使用的新功能:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()

#輸出:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs複製代碼

如今,您可能但願每次調用a_stand_alone_functiona_stand_alone_function_decorated都調用它。 這很簡單,只需a_stand_alone_function用如下方法返回的函數覆蓋my_shiny_new_decorator

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#輸出:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

#這正是裝飾器的工做!複製代碼

裝飾器神祕化

這裏展現一下使用裝飾器的語法:

@my_shiny_new_decorator
def another_stand_alone_function():
    print("Leave me alone")

another_stand_alone_function()  
#輸出:  
#Before the function runs
#Leave me alone
#After the function runs複製代碼

是的,僅此而已。@decorator只是實現如下目的的捷徑:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)複製代碼

裝飾器只是裝飾器設計模式的pythonic變體。 Python中嵌入了幾種經典的設計模式來簡化開發(例如迭代器)。

固然,您能夠累加裝飾器:

def bread(func):
    def wrapper():
        print("</''''''\>")
        func()
        print("<\______/>")
    return wrapper

def ingredients(func):
    def wrapper():
        print("#tomatoes#")
        func()
        print("~salad~")
    return wrapper

def sandwich(food="--ham--"):
    print(food)

sandwich()
#輸出: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#輸出:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>複製代碼

使用Python裝飾器語法:

@bread
@ingredients
def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>複製代碼

您設置裝飾器事項的順序是很重要的,如::

@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print(food)

strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>
# --ham--
#<\______/>
# ~salad~複製代碼

本文首發於BigYoung小站

相關文章
相關標籤/搜索