全網最詳細的裝飾器知識總結(一)

大家好诺擅,我是劍南。近期有不少人咨詢有關裝飾器的知識毙石,很多人對這一塊了解甚少,因此我便打算出兩期文章來專門寫裝飾器的知識颓遏,希望可以對大家?guī)韼椭?/p>

關于裝飾器介紹

python中裝飾器是指任何可以修改函數(shù)或類的可調用對象徐矩。它們允許一些類似于類似于其他語言的附加功能,例如將方法聲明為類或者是靜態(tài)方法叁幢。

類方法是在類是在類滤灯,而不是在特定實例調用的方法。靜態(tài)方法類似于類方法,但將應用于類的所有實例鳞骤,而不僅僅是特定的實例窒百。在python中,實例方法是處理OOP的傳統(tǒng)方法豫尽。

當一個函數(shù)被調用時篙梢,它被傳遞給裝飾器,裝飾器返回一個被修改過的函數(shù)/類美旧,這些修改過的對象通常包含調用最初的調用對象渤滞。

注意:裝飾器可以與函數(shù)或者方法一起使用,但通常都會簡稱它們?yōu)椤昂瘮?shù)”榴嗅,這使得描述更加簡潔妄呕。方法將在討論類的時候時使用。

簡單回顧函數(shù)

因為在處理裝飾器時嗽测,理解函數(shù)是如何工作的是非常重要的酝润,所以我們會快速瀏覽一下它們驻呐。首先习柠,我們需要記住蛉艾,python中一切都是對象,包括函數(shù)晤愧。

在python中使用def關鍵字并給出命名來創(chuàng)建函數(shù)蠢护。輸入參數(shù)是可選的,以下是一些基本參考功能养涮。

def func_foo():
    pass

實現(xiàn)方法

(1)函數(shù)可以有多個名稱,其實也可以這樣理解眉抬,除了函數(shù)名本身贯吓,還可以將一個函數(shù)分配給其他變量名,每個變量名與基礎函數(shù)具有相同的功能蜀变。

def first_func1(val):
    print(val)


new_name = first_func1

first_func1("hello world")

print('*'*50)

new_name('hello world too')

運行結果悄谐,如下所示:

hello world
**************************************************
hello world too

(2)創(chuàng)建函數(shù)時,參數(shù)是可以是其他函數(shù)库北,可以是自定義函數(shù)爬舰,也可以是內置函數(shù),例如map()和filter()寒瓦,使用這個特性來完成工作情屹。

具體代碼,如下所示:

def mult(x, y):
    return x * y

def div(x, y):
    return x/y


def math(func, x ,y):
    result = func(x, y)
    return result

val1 = math(mult, 4, 2)
print(val1)
print("*" * 50)
val2 = math(div, 4, 2)
print(val2)

運行結果杂腰,如下所示:

8
**************************************************
2.0

(3)像循環(huán)一樣垃你,函數(shù)也可以做嵌套操作。

具體代碼,如下所示:

def person(name):
    def greeting():
        return 'would you like some milk'

    greet = greeting() + name + '?'
    return greet


print(person('Sir Galahad'))

運行結果惜颇,如下所示:

would you like some milkSir Galahad?

(4)函數(shù)可以用作其他函數(shù)的參數(shù)皆刺。這是因為函數(shù)參數(shù)實際上是對對像的引用,由于函數(shù)是對象凌摄,因此函數(shù)(實際上是對函數(shù)對象的引用)可以用作參數(shù)羡蛾。

具體代碼,如下所示:

def greeting(name):
    return "'allo " + name

def call_me(func):
    nickname = 'mate'
    return func(nickname)

print(call_me(greeting))

運行結果锨亏,如下所示:

'allo mate

同樣痴怨,函數(shù)可以返回函數(shù),這是因為函數(shù)的返回值是對對象的引用屯伞。

具體代碼腿箩,如下所示:

def func_creator():
    def return_saying():
        return 'Blessed are the cheese makers'
    return return_saying


statement = func_creator()
print(statement())

運行結果,如下所示:

Blessed are the cheese makers

(6)嵌套函數(shù)可以訪問其父函數(shù)的作用域劣摇,這也稱為閉包珠移。我們必須認識到,這種訪問是只讀的末融,嵌套函數(shù)不能寫出钧惧,或將變量賦值給外部作用域。

在實際情況下勾习,這與哈函數(shù)變量分配參數(shù)沒有什么不同浓瞪,輸入參數(shù)只是將其傳遞給另一個封閉函數(shù),而不是變量巧婶。

具體代碼乾颁,如下所示:

def func_creator2(name):
    def greeting():
        return 'welcome ' + name
    return greeting


greet = func_creator2('Jack')
print(greet())

運行結果,如下所示:

welcome Jack

工作原理

函數(shù)及其面向對象的兄弟——方法艺栈,是許多編程語言中必不可少的元素英岭,它們允許代碼的復用,因為可以從代碼中的不同位置多次調用函數(shù)湿右。如果語言支持诅妹,甚至可以從不同的程序中調用它們,例如導入python毅人。

函數(shù)還允許抽象工作吭狡,在某種程度上,函數(shù)類似于黑盒丈莺。開發(fā)人員只需要知道為函數(shù)提供什么數(shù)據划煮、函數(shù)如何處理這些數(shù)據,以及是否返回值场刑。只要結果一致般此,就不需要知道函數(shù)的實現(xiàn)過程蚪战,內部代碼你是不知道的。

編寫一個沒有函數(shù)的程序是可行的铐懊,但是它要求對整個程序進行串行處理邀桑。所有需要重復的功能代碼必須每次都需要復制粘貼,這也是為什么最早的高級語言也包含了子程序科乎,子程序允許程序員跳出主程序的的邏輯來處理一些數(shù)據壁畸,然后返回到主程序。

在此之前茅茂,必須使用特殊的調用序列來實現(xiàn)子程序捏萍,以便將返回地址存儲到主程序中。

裝飾器簡介

有了以上的基礎空闲,接下來就可以價紹裝飾器了令杈。裝飾器是將一個函數(shù)包裝在另一個函數(shù)中,該函數(shù)以某種方式修改原始函數(shù)碴倾,例如添加功能逗噩、修改參數(shù)或結果,等待跌榔。

實現(xiàn)方法

(1)首先要定義裝飾器函數(shù)

# 定義裝飾器函數(shù)
def fun_decorator(some_funct):
    def wrapper():
        print('這是一個裝飾器')
        for i in range(10):
            print(i)
        print('裝飾器運行完畢异雁,返回原始的調度方法')
        print(some_funct())
    return wrapper

(2)定義主函數(shù)

# 定義主函數(shù)
def main_fun():
    text = '我是主程序'
    return text

(3)將主函數(shù)作為一個變量傳遞給裝飾器,并把這個裝飾器賦值給主函數(shù)

main_fun = fun_decorator(main_fun)

(4)調用主函數(shù)

main_fun()

(5)將上面的代碼進行整合僧须,如下所示:

# 定義裝飾器函數(shù)
def fun_decorator(some_funct):
    def wrapper():
        print('這是一個裝飾器')
        for i in range(10):
            print(i)
        print('裝飾器運行完畢纲刀,返回原始的調度方法')
        print(some_funct())
    return wrapper



# 定義主函數(shù)
def main_fun():
    text = '我是主程序'
    return text

main_fun = fun_decorator(main_fun)
main_fun()

(6)運行代碼,結果如下圖所示:

運行結果

(7)我們可以使用語法糖(@符號)來注釋主函數(shù)是由fun_decorator修改的担平,這樣可以消除main_fun = fun_decorator(main_fun)示绊,具體代碼,如下所示:

# 定義裝飾器函數(shù)
def fun_decorator(some_funct):
    def wrapper():
        print('這是一個裝飾器')
        for i in range(10):
            print(i)
        print('裝飾器運行完畢暂论,返回原始的調度方法')
        print(some_funct())
    return wrapper

@fun_decorator
def main_fun():
    text = '我是主程序'
    return text


main_fun()

(8)修改代碼后耻台,運行結果一致,如下圖所示:

運行結果

工作原理

工作原理

當一個帶有裝飾器的函數(shù)被調用空另,裝飾器函數(shù)會捕捉到這個調用,然后裝飾器函數(shù)會執(zhí)行它的工作蹋砚。在它完成之后扼菠,移交給原始函數(shù),從而完成任務坝咐。

語法糖是編程語言中的一種特殊語法循榆,旨在通過使代碼更易于讀或寫,令程序員的工作更輕松墨坚。語法糖表達式是通過查看糖丟失時代碼功能是否會一起丟失來識別的秧饮。

使用函數(shù)裝飾器

函數(shù)裝飾器顯然適用于函數(shù)映挂。@fun_decorator裝飾器行放在函數(shù)定義之前的行上。語法糖接收一個函數(shù)盗尸,并通過另一個函數(shù)自動運行其結果柑船。在處理結束時,原始函數(shù)調用的名稱應用于最終結果泼各。對于系統(tǒng)來說鞍时,它看起來像直接提供結果的原始函數(shù)調用。(但扣蜻,其實并不是)下面是裝飾器的演示逆巍。

@fun_decorator
def my_function():
    pass

當python解釋器到達此代碼塊時,將處理my_function()并將結果傳遞給@fun_decorator所指向的函數(shù)莽使。裝飾器函數(shù)將被處理锐极,結果替換為原來的my_function()結果。從本質上說芳肌,裝飾器劫持了函數(shù)的調用灵再,修改原始結果,并將修改結果替換為原始函數(shù)提供的結果庇勃。

修改裝飾器代碼檬嘀,可以采用管理或增加原始調用的形式。一旦一個函數(shù)完成它的工作责嚷,裝飾器就接管并對原始結果進行處理鸳兽,返回修改后的代碼。

實現(xiàn)方法

(1)我們必須要明確裝飾器要做什么罕拂。對于下面的例子揍异,裝飾器函數(shù)將查看傳遞給函數(shù)的參數(shù),并檢查傳遞的值是否是整數(shù)爆班。

(2)編寫裝飾器函數(shù)

def arg_check(func):
    def wrapper(num):
        if type(num) != int:
            raise TypeError('參數(shù)不是一個整數(shù)')
        elif num <= 0:
            raise ValueError('參數(shù)不是一個正數(shù)')
        else:
            return func(num)
    return wrapper

(3)編寫需要修飾的函數(shù)

@arg_check
def circle_measures(radius):
    circumference = 2 * pi *radius
    area = pi * radius * radius
    diameter = 2 * radius
    return (circumference, area, diameter)

(4)調用并打印有關圓的值

circumference, area, diameter = circle_measures(6)
print('circumference is ', circumference, '\n', 'area is ', area, '\n', 'diameter is ', diameter)

注意:在實際情況下衷掷,這與為函數(shù)變量分配參數(shù)沒有什么不同。輸入參數(shù)只是將其傳遞給另一個封閉函數(shù)柿菩,而不是變量戚嗅。

例如,上面的代碼circle_measures(6)里面的6是傳遞給裝飾器中的wrapper()函數(shù)枢舶。

使用類裝飾器

從python的2.6版本開始懦胞,裝飾器就被用于處理類。在這種情況下凉泄,裝飾器不僅可以應用于函數(shù)躏尉,還可以用于單個實例或類本身。它們使開發(fā)人員的意圖更加明顯后众,在調用方法或處理對象時胀糜,它們可以用于最小化錯誤颅拦。

實現(xiàn)方法

(1)類方法也可以使用裝飾器。實例方法是最常見的方法形式教藻,即類中的函數(shù)距帅。具體代碼,如下所示:

class Cat(object):
    """docstring for Cat"""
    def __init__(self, breed, age):
        super(Cat, self).__init__()
        self.breed = breed
        self.age = age


    def cat_age(self):
        return self.age


    def breed(self):
        return self.breed

    def __repr__(self):
        return '{breed}, {age}'.format(breed = self.breed, age = self.age)

(2)要使用這個類怖竭,需要創(chuàng)建一個Cat實例锥债,提供初始參數(shù)。

chip = Cat('domestic shorthair', 4)

(3)接下來痊臭,調用類方法哮肚,使其可以正常運行。

print(chip.age)
print(chip.breed)

(4)注意广匙,這些方法被綁定到了一個實例chip中允趟,因此,它們不能再泛型Cat類上調用鸦致。

print(Cat.age)

上面的寫法是錯誤的潮剪。

(5)靜態(tài)方法是應用于所有實例的方法。它們用方法定義之前的@staticmethod裝飾器標識分唾。另外抗碰,方法本身不需要定義self參數(shù)。

@staticmethod
def cry():
    return '橘貓'

(6)靜態(tài)方法可以應用于實例和類本身绽乔,具體代碼弧蝇,如下所示:

class Cat(object):
    """docstring for Cat"""
    def __init__(self, breed, age):
        super(Cat, self).__init__()
        self.breed = breed
        self.age = age


    def cat_age(self):
        return self.age


    def breed(self):
        return self.breed

    @staticmethod
    def cry():
        return '橘貓'

    def __repr__(self):
        return '{breed}, {age}'.format(breed = self.breed, age = self.age)



chip = Cat('橘貓', 10)
print(chip.cry)
print(chip.cry())

print(Cat.cry)
print(Cat.cry())

運行結果,如下所示:

<function Cat.cry at 0x0000017AF837D280>
橘貓
<function Cat.cry at 0x0000017AF837D280>
橘貓

當調用的是不帶括號的靜態(tài)方法會返回方法的內存位置折砸。方法沒有綁定到實例看疗,但是類頁可以使用它。只有在使用括號時睦授,才會顯示正確的返回對象两芳。

(7)類方法由創(chuàng)建方法之前的@classmethod標識。此外去枷,方法參數(shù)是cls而不是self怖辆。可以在前面的例子中的靜態(tài)方法之后添加以下代碼删顶。

class Cat(object):
    """docstring for Cat"""
    def __init__(self, breed, age):
        super(Cat, self).__init__()
        self.breed = breed
        self.age = age


    def cat_age(self):
        return self.age


    def breed(self):
        return self.breed

    @staticmethod
    def cry():
        return '橘貓'


    @classmethod
    def type(cls):
        if cls.__name__ == "Cat":
            return "some sort of domestic cat"
        else:
            return cls.__name__

    def __repr__(self):
        return '{breed}, {age}'.format(breed = self.breed, age = self.age)



chip = Cat('橘貓',1)
print(chip.type())

運行結果疗隶,如下所示:

some sort of domestic cat

在創(chuàng)建實例時,將檢查它來自哪一個類翼闹。如果泛型Cat類是生成器,則會輸出一條消息蒋纬,如果使用Cat類的子類猎荠,則輸出該類名稱坚弱。

最后

第一期的裝飾器知識已經寫完了,但還沒有結束关摇。

敬請期待我下一期寫的裝飾器知識荒叶。

我想對觀看到這的小伙伴們說,文章的每一個字都是我用心敲出來的输虱,希望你可以點個【再看】些楣,讓我知道,你就是那個陪我一起努力的人宪睹。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末愁茁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亭病,更是在濱河造成了極大的恐慌鹅很,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罪帖,死亡現(xiàn)場離奇詭異促煮,居然都是意外死亡,警方通過查閱死者的電腦和手機整袁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門菠齿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坐昙,你說我怎么就攤上這事绳匀。” “怎么了民珍?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵襟士,是天一觀的道長。 經常有香客問我嚷量,道長陋桂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任蝶溶,我火速辦了婚禮嗜历,結果婚禮上,老公的妹妹穿的比我還像新娘抖所。我一直安慰自己梨州,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布田轧。 她就那樣靜靜地躺著暴匠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪傻粘。 梳的紋絲不亂的頭發(fā)上每窖,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天帮掉,我揣著相機與錄音,去河邊找鬼窒典。 笑死蟆炊,一個胖子當著我的面吹牛,可吹牛的內容都是我干的瀑志。 我是一名探鬼主播涩搓,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劈猪!你這毒婦竟也來了昧甘?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤岸霹,失蹤者是張志新(化名)和其女友劉穎疾层,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡避,經...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡痛黎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了刮吧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湖饱。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杀捻,靈堂內的尸體忽然破棺而出井厌,到底是詐尸還是另有隱情,我是刑警寧澤致讥,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布仅仆,位于F島的核電站,受9級特大地震影響垢袱,放射性物質發(fā)生泄漏墓拜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一请契、第九天 我趴在偏房一處隱蔽的房頂上張望咳榜。 院中可真熱鬧,春花似錦爽锥、人聲如沸涌韩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臣樱。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雇毫,已是汗流浹背奢啥。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘴拢,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓寂纪,卻偏偏與公主長得像席吴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捞蛋,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內容