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

資訊專欄INFORMATION COLUMN

Python知識(shí)點(diǎn):理解和使用裝飾器 @decorator

cyqian / 863人閱讀

摘要:使用類裝飾器,優(yōu)點(diǎn)是靈活性大,高內(nèi)聚,封裝性。不過(guò)不用擔(dān)心,有,本身也是一個(gè)裝飾器,它的作用就是把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息。


Python的裝飾器(decorator)是一個(gè)很棒的機(jī)制,也是熟練運(yùn)用Python的必殺技之一。裝飾器,顧名思義,就是用來(lái)裝飾的,它裝飾的是一個(gè)函數(shù),保持被裝飾函數(shù)的原有功能,再裝飾上(添油加醋)一些其它功能,并返回帶有新增功能的函數(shù)對(duì)象,所以裝飾器本質(zhì)上是一個(gè)返回函數(shù)對(duì)象的函數(shù)(確切的說(shuō),裝飾器應(yīng)該是可調(diào)用對(duì)象,除了函數(shù),類也可以作為裝飾器)。

在編程過(guò)程中,我們經(jīng)常遇到這樣的場(chǎng)景:登錄校驗(yàn),權(quán)限校驗(yàn),日志記錄等,這些功能代碼在各個(gè)環(huán)節(jié)都可能需要,但又十分雷同,通過(guò)裝飾器來(lái)抽象、剝離這部分代碼可以很好解決這類場(chǎng)景。

裝飾器是什么?

要理解Python的裝飾器,首先我們先理解一下Python的函數(shù)對(duì)象。我們知道,在Python里一切都是對(duì)象,函數(shù)也不例外,函數(shù)是第一類對(duì)象(first-class objects),它可以賦值給變量,也可以作為list的元素,還可以作為參數(shù)傳遞給其它函數(shù)。

函數(shù)可以被變量引用

定義一個(gè)簡(jiǎn)單的函數(shù):

def say_hi():
    print("Hi!")
say_hi()
# Output: Hi!

我們可以通過(guò)另外一個(gè)變量say_hi2來(lái)引用say_hi函數(shù):

say_hi2 = say_hi
print(say_hi2)
# Output: 

say_hi2()
# Output: Hi!

上面的語(yǔ)句中say_hi2 和 say_hi 指向了同樣的函數(shù)定義,二者的執(zhí)行結(jié)果也相同。

函數(shù)可以作為參數(shù)傳遞給其它函數(shù)

def say_more(say_hi_func):
    print("More")
    say_hi_func()

say_more(say_hi)
# Output:
#     More
#     Hi

在上面的例子中,我們把say_hi函數(shù)當(dāng)做參數(shù)傳遞給say_more函數(shù),say_hi 被變量 say_hi_func 引用。

函數(shù)可以定義在其它函數(shù)內(nèi)部

def say_hi():
    print("Hi!")
    def say_name():
        print("Tom")
    say_name()

say_hi()
# Output:
#     Hi!
#     Tom

say_name() # 報(bào)錯(cuò)

上述代碼中,我們?cè)趕ay_hi()函數(shù)內(nèi)部定義了另外一個(gè)函數(shù)say_name()。say_name()只在say_hi函數(shù)內(nèi)部可見(即,它的作用域在say_hi函數(shù)內(nèi)部),在say_hi外包調(diào)用時(shí)就會(huì)出錯(cuò)。

函數(shù)可以返回其它函數(shù)的引用

def say_hi():
    print("Hi!")
    def say_name():
        print("Tom")
    return say_name

say_name_func = say_hi()
# 打印Hi!,并返回say_name函數(shù)對(duì)象
# 并賦值給say_name_func

say_name_func()
# 打印 Tom

上面的例子,say_hi函數(shù)返回了其內(nèi)部定義的函數(shù)say_name的引用。這樣在say_hi函數(shù)外部也可以使用say_name函數(shù)了。

前面我們理解了函數(shù),這有助于我們接下來(lái)弄明白裝飾器。

裝飾器(Decorator)

裝飾器是可調(diào)用對(duì)象(callable objects),它用來(lái)修改函數(shù)或類。
可調(diào)用對(duì)象就是可以接受某些參數(shù)并返回某些對(duì)象的對(duì)象。Python里的函數(shù)和類都是可調(diào)用對(duì)象。

函數(shù)裝飾器,就是接受函數(shù)作為參數(shù),并對(duì)函數(shù)參數(shù)做一些包裝,然后返回增加了包裝的函數(shù),即生成了一個(gè)新函數(shù)。

讓我們看看下面這個(gè)例子:

def decorator_func(some_func):
  # define another wrapper function which modifies some_func
  def wrapper_func():
    print("Wrapper function started")
    
    some_func()
    
    print("Wrapper function ended")
    
  return wrapper_func # Wrapper function add something to the passed function and decorator returns the wrapper function
    
def say_hello():
  print ("Hello")
  
say_hello = decorator_func(say_hello)

say_hello()

# Output:
#  Wrapper function started
#  Hello
#  Wrapper function ended

上面例子中,decorator_func 就是定義的裝飾器函數(shù),它接受some_func作為參數(shù)。它定義了一個(gè)wrapper_func函數(shù),該函數(shù)調(diào)用了some_func但也增加了一些自己的代碼。

上面代碼中使用裝飾器的方法看起來(lái)有點(diǎn)復(fù)雜,其實(shí)真正的裝飾器的Python語(yǔ)法是這樣的:

裝飾器的Python語(yǔ)法

@decorator_func
def say_hi():
    print "Hi!"

@ 符合是裝飾器的語(yǔ)法糖,在定義函數(shù)say_hi時(shí)使用,避免了再一次的賦值語(yǔ)句。
上面的語(yǔ)句等同于:

def say_hi():
    print "Hi!"
say_hi = decorator_func(say_hi)

裝飾器的順序

@a
@b
@c
def foo():
    print("foo")

# 等同于:
foo = a(b(c(foo)))

帶參數(shù)函數(shù)的裝飾器

def decorator_func(some_func):
    def wrapper_func(*args, **kwargs):
        print("Wrapper function started")
        some_func(*args, **kwargs)
        print("Wrapper function ended")
    
    return wrapper_func

@decorator_func    
def say_hi(name):
    print ("Hi!" + name)

上面代碼中,say_hi函數(shù)帶有一個(gè)參數(shù)。通常情況下,不同功能的函數(shù)可以有不同類別、不同數(shù)量的參數(shù),在寫wrapper_func的時(shí)候,我們不確定參數(shù)的名稱和數(shù)量,可以通過(guò)args 和 *kwargs 來(lái)引用函數(shù)參數(shù)。

帶參數(shù)的裝飾器

不僅被裝飾的函數(shù)可以帶參數(shù),裝飾器本身也可以帶參數(shù)。參考下面的例子:

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name="foo"):
    print("i am %s" % name)

簡(jiǎn)單來(lái)說(shuō),帶參數(shù)的裝飾器就是在沒(méi)有參數(shù)的裝飾器外面再嵌套一個(gè)參數(shù)的函數(shù),該函數(shù)返回那個(gè)無(wú)參數(shù)裝飾器即可。

類作為裝飾器

前面我們提到裝飾器是可調(diào)用對(duì)象。在Python里面,除了函數(shù),類也是可調(diào)用對(duì)象。使用類裝飾器,優(yōu)點(diǎn)是靈活性大,高內(nèi)聚,封裝性。通過(guò)實(shí)現(xiàn)類內(nèi)部的__call__方法,當(dāng)使用 @ 語(yǔ)法糖把裝飾器附加到函數(shù)上時(shí),就會(huì)調(diào)用此方法。

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

def __call__(self):
    print ("class decorator runing")
    self._func()
    print ("class decorator ending")

@Foo
def say_hi():
    print("Hi!")

say_hi()
# Output:
# class decorator running
# Hi!
# class decorator ending

functools.wraps

使用裝飾器極大地復(fù)用了代碼,但是他有一個(gè)缺點(diǎn)就是原函數(shù)的元信息不見了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看看下面例子:

def decorator_func(some_func):
    def wrapper_func(*args, **kwargs):
        print("Wrapper function started")
        some_func(*args, **kwargs)
        print("Wrapper function ended")
    
    return wrapper_func

@decorator_func    
def say_hi(name):
    """Say hi to somebody"""
    print ("Hi!" + name)

print(say_hi.__name__)  # Output: wrapper_func
print(say_hi.__doc__)   # Output: None

可以看到,say_hi函數(shù)被wrapper_func函數(shù)取代,它的__name__ 和 docstring 也自然是wrapper_func函數(shù)的了。
不過(guò)不用擔(dān)心,Python有functools.wraps,wraps本身也是一個(gè)裝飾器,它的作用就是把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息。

from functools import wraps
def decorator_func(some_func):
    @wraps(func)
    def wrapper_func(*args, **kwargs):
        print("Wrapper function started")
        some_func(*args, **kwargs)
        print("Wrapper function ended")
    
    return wrapper_func

@decorator_func    
def say_hi(name):
    """Say hi to somebody"""
    print ("Hi!" + name)

print(say_hi.__name__)  # Output: say_hi
print(say_hi.__doc__)   # Output: Say hi to somebody

類的內(nèi)置裝飾器

類屬性@property
靜態(tài)方法@staticmethod
類方法@classmethod

通常,我們需要先實(shí)例化一個(gè)類的對(duì)象,再調(diào)用其方法。
若類的方法使用了@staticmethod或@classmethod,就可以不需要實(shí)例化,直接類名.方法名()來(lái)調(diào)用。
從使用上來(lái)看,@staticmethod不需要指代自身對(duì)象的self或指代自身類的cls參數(shù),就跟使用普通函數(shù)一樣。@classmethod不需要self參數(shù),但第一個(gè)參數(shù)必須是指代自身類的cls參數(shù)。如果在@staticmethod中要調(diào)用到這個(gè)類的一些屬性方法,只能直接類名.屬性名,或類名.方法名的方式。
而@classmethod因?yàn)槌钟衏ls參數(shù),可以來(lái)調(diào)用類的屬性,類的方法,實(shí)例化對(duì)象等。

總結(jié)

通過(guò)認(rèn)識(shí)Python的函數(shù),我們逐步弄清了裝飾器的來(lái)龍去脈。裝飾器是代碼復(fù)用的好工具,在編程過(guò)程中可以在適當(dāng)?shù)膱?chǎng)景用多多使用。

我在我的個(gè)人博客“猿人學(xué)網(wǎng)站”和公眾號(hào)“猿人學(xué)Python”上寫Python教程,有興趣的可以關(guān)注公眾號(hào)和網(wǎng)站。

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

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

相關(guān)文章

  • Python 裝飾使用指南

    摘要:裝飾器是可調(diào)用的對(duì)象,其參數(shù)是另一個(gè)函數(shù)被裝飾的函數(shù)。第二大特性是,裝飾器在加載模塊時(shí)立即執(zhí)行。另一個(gè)常見的裝飾器是,它的作用是協(xié)助構(gòu)建行為良好的裝飾器。 裝飾器是可調(diào)用的對(duì)象,其參數(shù)是另一個(gè)函數(shù)(被裝飾的函數(shù))。 裝飾器基礎(chǔ)知識(shí) 首先看一下這段代碼 def deco(fn): print I am %s! % fn.__name__ @deco def func(): ...

    NeverSayNever 評(píng)論0 收藏0
  • Python 函數(shù)式編程、裝飾以及一些相關(guān)概念簡(jiǎn)介

    摘要:重寫內(nèi)建名字空間中的函數(shù)閉包閉包是詞法閉包的簡(jiǎn)稱。另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 Python 中的 Decorator(裝飾器) 是對(duì)一個(gè)函數(shù)或者方法的封裝,從而使其可以完成一些與自身功能無(wú)關(guān)的工作。 預(yù)備知識(shí) 一切皆對(duì)象 在 Python 中,所有的一切都被視為對(duì)象,任何的變量、函數(shù)、類等都是 object 的子類。因此除了變量之外,函數(shù)和類等也可以...

    Jinkey 評(píng)論0 收藏0
  • 簡(jiǎn)單理解Python裝飾

    摘要:下面我們一起拋去無(wú)關(guān)概念,簡(jiǎn)單地理解下的裝飾器。用函數(shù)實(shí)現(xiàn)裝飾器裝飾器要求入?yún)⑹呛瘮?shù)對(duì)象,返回值是函數(shù)對(duì)象,嵌套函數(shù)完全能勝任。為了對(duì)調(diào)用方透明,裝飾器返回的對(duì)象要偽裝成被裝飾的函數(shù)。 來(lái)源:http://www.lightxue.com/under... ???????Python有大量強(qiáng)大又貼心的特性,如果要列個(gè)最受歡迎排行榜,那么裝飾器絕對(duì)會(huì)在其中。???????剛接觸裝飾器,會(huì)...

    Meils 評(píng)論0 收藏0
  • Python: 會(huì)打扮的裝飾

    摘要:一般情況下,我們使用裝飾器提供的語(yǔ)法糖,來(lái)簡(jiǎn)化上面的寫法像上面的情況,可以動(dòng)態(tài)修改函數(shù)或類功能的函數(shù)就是裝飾器。本文標(biāo)題為會(huì)打扮的裝飾器本文鏈接為參考資料修飾器的函數(shù)式編程中的裝飾器介紹思誠(chéng)之道裝飾器入門與提高賴明星 裝飾器 我們知道,在 Python 中,我們可以像使用變量一樣使用函數(shù): 函數(shù)可以被賦值給其他變量 函數(shù)可以被刪除 可以在函數(shù)里面再定義函數(shù) 函數(shù)可以作為參數(shù)傳遞給另外...

    blastz 評(píng)論0 收藏0
  • python裝飾詳解

    摘要:為了避免重復(fù)調(diào)用,可以適當(dāng)?shù)刈鼍彺?,的裝飾器可以完美的完成這一任務(wù)。這意味著我們可以為方法創(chuàng)建裝飾器,只是要記得考慮。裝飾器封裝了函數(shù),這使得調(diào)試函數(shù)變得困難。另外,使用裝飾器去管理緩存和權(quán)限。 原文地址 之前用python簡(jiǎn)單寫了一下斐波那契數(shù)列的遞歸實(shí)現(xiàn)(如下),發(fā)現(xiàn)運(yùn)行速度很慢。 def fib_direct(n): assert n > 0, invalid n ...

    maybe_009 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<