成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python: 會打扮的裝飾器

blastz / 772人閱讀

摘要:一般情況下,我們使用裝飾器提供的語法糖,來簡化上面的寫法像上面的情況,可以動態(tài)修改函數(shù)或類功能的函數(shù)就是裝飾器。本文標(biāo)題為會打扮的裝飾器本文鏈接為參考資料修飾器的函數(shù)式編程中的裝飾器介紹思誠之道裝飾器入門與提高賴明星

裝飾器

我們知道,在 Python 中,我們可以像使用變量一樣使用函數(shù):

函數(shù)可以被賦值給其他變量

函數(shù)可以被刪除

可以在函數(shù)里面再定義函數(shù)

函數(shù)可以作為參數(shù)傳遞給另外一個函數(shù)

函數(shù)可以作為另一個函數(shù)的返回

簡而言之,函數(shù)就是一個對象。

對一個簡單的函數(shù)進(jìn)行裝飾

為了更好地理解裝飾器,我們先從一個簡單的例子開始,假設(shè)有下面的函數(shù):

def hello():
    return "hello world"

現(xiàn)在我們想增強 hello() 函數(shù)的功能,希望給返回加上 HTML 標(biāo)簽,比如 hello world,但是有一個要求,不改變原來 hello() 函數(shù)的定義。這里當(dāng)然有很多種方法,下面給出一種跟本文相關(guān)的方法:

def makeitalic(func):
    def wrapped():
        return "" + func() + ""
    return wrapped

在上面的代碼中,我們定義了一個函數(shù) makeitalic,該函數(shù)有一個參數(shù) func,它是一個函數(shù);在 makeitalic 函數(shù)里面我們又定義了一個內(nèi)部函數(shù) wrapped,并將該函數(shù)作為返回。

現(xiàn)在,我們就可以不改變 hello() 函數(shù)的定義,給返回加上 HTML 標(biāo)簽了:

>>> hello = makeitalic(hello)  # 將 hello 函數(shù)傳給 makeitalic
>>> hello()
"hello world"

在上面,我們將 hello 函數(shù)傳給 makeitalic,再將返回賦給 hello,此時調(diào)用 hello() 就得到了我們想要的結(jié)果。

不過要注意的是,由于我們將 makeitalic 的返回賦給了 hello,此時 hello() 函數(shù)仍然存在,但是它的函數(shù)名不再是 hello 了,而是 wrapped,正是 makeitalic 返回函數(shù)的名稱,可以驗證一下:

>>> hello.__name__
"wrapped"

對于這個小瑕疵,后文將會給出解決方法。

現(xiàn)在,我們梳理一下上面的例子,為了增強原函數(shù) hello 的功能,我們定義了一個函數(shù),它接收原函數(shù)作為參數(shù),并返回一個新的函數(shù),完整的代碼如下:

def makeitalic(func):
    def wrapped():
        return "" + func() + ""
    return wrapped

def hello():
    return "hello world"

hello = makeitalic(hello)

事實上,makeitalic 就是一個裝飾器(decorator),它『裝飾』了函數(shù) hello,并返回一個函數(shù),將其賦給 hello。

一般情況下,我們使用裝飾器提供的 @ 語法糖(Syntactic Sugar),來簡化上面的寫法:

def makeitalic(func):
    def wrapped():
        return "" + func() + ""
    return wrapped

@makeitalic
def hello():
    return "hello world"

像上面的情況,可以動態(tài)修改函數(shù)(或類)功能的函數(shù)就是裝飾器。本質(zhì)上,它是一個高階函數(shù),以被裝飾的函數(shù)(比如上面的 hello)為參數(shù),并返回一個包裝后的函數(shù)(比如上面的 wrapped)給被裝飾函數(shù)(hello)。

裝飾器的使用形式

裝飾器的一般使用形式如下:

@decorator
def func():
    pass

等價于下面的形式:

def func():
    pass
func = decorator(func)

裝飾器可以定義多個,離函數(shù)定義最近的裝飾器先被調(diào)用,比如:

@decorator_one
@decorator_two
def func():
    pass

等價于:

def func():
    pass

func = decorator_one(decorator_two(func))

裝飾器還可以帶參數(shù),比如:

@decorator(arg1, arg2)
def func():
    pass

等價于:

def func():
    pass

func = decorator(arg1, arg2)(func)

下面我們再看一些具體的例子,以加深對它的理解。

對帶參數(shù)的函數(shù)進(jìn)行裝飾

前面的例子中,被裝飾的函數(shù) hello() 是沒有帶參數(shù)的,我們看看被裝飾函數(shù)帶參數(shù)的情況。對前面例子中的 hello() 函數(shù)進(jìn)行改寫,使其帶參數(shù),如下:

def makeitalic(func):
    def wrapped(*args, **kwargs):
        ret = func(*args, **kwargs)
        return "" + ret + ""
    return wrapped

@makeitalic
def hello(name):
    return "hello %s" % name
    
@makeitalic
def hello2(name1, name2):
    return "hello %s, %s" % (name1, name2)

由于函數(shù) hello 帶參數(shù),因此內(nèi)嵌包裝函數(shù) wrapped 也做了一點改變:

內(nèi)嵌包裝函數(shù)的參數(shù)傳給了 func,即被裝飾函數(shù),也就是說內(nèi)嵌包裝函數(shù)的參數(shù)跟被裝飾函數(shù)的參數(shù)對應(yīng),這里使用了 (*args, **kwargs),是為了適應(yīng)可變參數(shù)。

看看使用:

>>> hello("python")
"hello python"
>>> hello2("python", "java")
"hello python, java"
帶參數(shù)的裝飾器

上面的例子,我們增強了函數(shù) hello 的功能,給它的返回加上了標(biāo)簽 ...,現(xiàn)在,我們想改用標(biāo)簽 ...

...

。是不是要像前面一樣,再定義一個類似 makeitalic 的裝飾器呢?其實,我們可以定義一個函數(shù),將標(biāo)簽作為參數(shù),返回一個裝飾器,比如:

def wrap_in_tag(tag):
    def decorator(func):
        def wrapped(*args, **kwargs):
            ret = func(*args, **kwargs)
            return "<" + tag + ">" + ret + ""
        return wrapped

    return decorator

現(xiàn)在,我們可以根據(jù)需要生成想要的裝飾器了:

makebold = wrap_in_tag("b")  # 根據(jù) "b" 返回 makebold 生成器

@makebold
def hello(name):
    return "hello %s" % name
    
>>> hello("world")
"hello world"

上面的形式也可以寫得更加簡潔:

@wrap_in_tag("b")
def hello(name):
    return "hello %s" % name

這就是帶參數(shù)的裝飾器,其實就是在裝飾器外面多了一層包裝,根據(jù)不同的參數(shù)返回不同的裝飾器。

多個裝飾器

現(xiàn)在,讓我們來看看多個裝飾器的例子,為了簡單起見,下面的例子就不使用帶參數(shù)的裝飾器。

def makebold(func):
    def wrapped():
        return "" + func() + ""

    return wrapped

def makeitalic(func):
    def wrapped():
        return "" + func() + ""

    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

上面定義了兩個裝飾器,對 hello 進(jìn)行裝飾,上面的最后幾行代碼相當(dāng)于:

def hello():
    return "hello world"

hello = makebold(makeitalic(hello))

調(diào)用函數(shù) hello

>>> hello()
"hello world"
基于類的裝飾器

前面的裝飾器都是一個函數(shù),其實也可以基于類定義裝飾器,看下面的例子:

class Bold(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return "" + self.func(*args, **kwargs) + ""

@Bold
def hello(name):
    return "hello %s" % name

>>> hello("world")
"hello world"

可以看到,類 Bold 有兩個方法:

__init__():它接收一個函數(shù)作為參數(shù),也就是被裝飾的函數(shù)

__call__():讓類對象可調(diào)用,就像函數(shù)調(diào)用一樣,在調(diào)用被裝飾函數(shù)時被調(diào)用

還可以讓類裝飾器帶參數(shù):

class Tag(object):
    def __init__(self, tag):
        self.tag = tag

    def __call__(self, func):
        def wrapped(*args, **kwargs):
            return "<{tag}>{res}".format(
                res=func(*args, **kwargs), tag=self.tag
            )
        return wrapped

@Tag("b")
def hello(name):
    return "hello %s" % name

需要注意的是,如果類裝飾器有參數(shù),則 __init__ 接收參數(shù),而 __call__ 接收 func。

裝飾器的副作用

前面提到,使用裝飾器有一個瑕疵,就是被裝飾的函數(shù),它的函數(shù)名稱已經(jīng)不是原來的名稱了,回到最開始的例子:

def makeitalic(func):
    def wrapped():
        return "" + func() + ""
    return wrapped

@makeitalic
def hello():
    return "hello world"

函數(shù) hellomakeitalic 裝飾后,它的函數(shù)名稱已經(jīng)改變了:

>>> hello.__name__
"wrapped"

為了消除這樣的副作用,Python 中的 functool 包提供了一個 wraps 的裝飾器:

from functools import wraps

def makeitalic(func):
    @wraps(func)       # 加上 wraps 裝飾器
    def wrapped():
        return "" + func() + ""
    return wrapped

@makeitalic
def hello():
    return "hello world"

>>> hello.__name__
"hello"
小結(jié)

本質(zhì)上,裝飾器就是一個返回函數(shù)的高階函數(shù)。

裝飾器可以動態(tài)地修改一個類或函數(shù)的功能,通過在原有的類或者函數(shù)上包裹一層修飾類或修飾函數(shù)實現(xiàn)。

事實上,裝飾器就是閉包的一種應(yīng)用,但它比較特別,接收被裝飾函數(shù)為參數(shù),并返回一個函數(shù),賦給被裝飾函數(shù),閉包則沒這種限制。

本文由 funhacks 發(fā)表于個人博客,采用 Creative Commons BY-NC-ND 4.0(自由轉(zhuǎn)載-保持署名-非商用-禁止演繹)協(xié)議發(fā)布。
非商業(yè)轉(zhuǎn)載請注明作者及出處。商業(yè)轉(zhuǎn)載請聯(lián)系作者本人。
本文標(biāo)題為: Python: 會打扮的裝飾器
本文鏈接為: https://funhacks.net/2016/11/...

參考資料

Python修飾器的函數(shù)式編程 - coolshell

How can I make a chain of function decorators in Python? - Stack Overflow

Python中的裝飾器介紹 – 思誠之道

python裝飾器入門與提高 | 賴明星

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44297.html

相關(guān)文章

  • Python】一文弄懂python裝飾(附源碼例子)

    摘要:裝飾器的使用符合了面向?qū)ο缶幊痰拈_放封閉原則。三簡單的裝飾器基于上面的函數(shù)執(zhí)行時間的需求,我們就手寫一個簡單的裝飾器進(jìn)行實現(xiàn)。函數(shù)體就是要實現(xiàn)裝飾器的內(nèi)容。類裝飾器的實現(xiàn)是調(diào)用了類里面的函數(shù)。類裝飾器的寫法比我們裝飾器函數(shù)的寫法更加簡單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...

    liuchengxu 評論0 收藏0
  • Python函數(shù)裝飾和閉包

    摘要:變量查找規(guī)則在中一個變量的查找順序是局部環(huán)境,閉包,全局,內(nèi)建閉包引用了自由變量的函數(shù)。閉包的作用閉包的最大特點是可以將父函數(shù)的變量與內(nèi)部函數(shù)綁定,并返回綁定變量后的函數(shù),此時即便生成閉包的環(huán)境父函數(shù)已經(jīng)釋放,閉包仍然存在。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之函數(shù)篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點: 1、掌握裝飾器的本質(zhì)、功...

    caozhijian 評論0 收藏0
  • Python學(xué)習(xí)之路26-函數(shù)裝飾和閉包

    摘要:初步認(rèn)識裝飾器函數(shù)裝飾器用于在源代碼中標(biāo)記函數(shù),以某種方式增強函數(shù)的行為。函數(shù)裝飾器在導(dǎo)入模塊時立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時運行。只有涉及嵌套函數(shù)時才有閉包問題。如果想保留函數(shù)原本的屬性,可以使用標(biāo)準(zhǔn)庫中的裝飾器。 《流暢的Python》筆記本篇將從最簡單的裝飾器開始,逐漸深入到閉包的概念,然后實現(xiàn)參數(shù)化裝飾器,最后介紹標(biāo)準(zhǔn)庫中常用的裝飾器。 1. 初步認(rèn)識裝飾器 函數(shù)裝飾...

    sunny5541 評論0 收藏0
  • Python裝飾

    摘要:一引用書流暢的書二基本概念問題裝飾器是什么解答嚴(yán)格來說,裝飾器只是語法糖,裝飾器是可調(diào)用的對象,可以像常規(guī)的可調(diào)用對象那樣調(diào)用,特殊的地方是裝飾器的參數(shù)是一個函數(shù)問題裝飾器有什么特性解答裝飾器有個特性,一是可以把被裝飾的函數(shù)替換成其他函數(shù), 一, 引用 [書] 流暢的Python [書] Effective Python 二, 基本概念 showImg(https://segme...

    aisuhua 評論0 收藏0
  • Python裝飾vs裝飾模式

    摘要:希望引以為戒鄭傳裝飾模式如果你了解,你肯定聽過裝飾器模式。在面向?qū)ο笾?,裝飾模式指動態(tài)地給一個對象添加一些額外的職責(zé)。就增加一些功能來說,裝飾模式比生成子類更為靈活。 漫談 如果作為一個Python入門,不了解Python裝飾器也沒什么,但是如果作為一個中級Python開發(fā)人員,如果再不對python裝飾器熟稔于心的話,那么可能并沒有量變積累到質(zhì)變。 我以前也看過很多講python 裝...

    stackvoid 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<