国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

簡單理解Python裝飾器

Meils / 3088人閱讀

摘要:下面我們一起拋去無關(guān)概念,簡單地理解下的裝飾器。用函數(shù)實現(xiàn)裝飾器裝飾器要求入?yún)⑹呛瘮?shù)對象,返回值是函數(shù)對象,嵌套函數(shù)完全能勝任。為了對調(diào)用方透明,裝飾器返回的對象要偽裝成被裝飾的函數(shù)。

來源:http://www.lightxue.com/under...

???????Python有大量強大又貼心的特性,如果要列個最受歡迎排行榜,那么裝飾器絕對會在其中。
???????剛接觸裝飾器,會覺得代碼不多卻難以理解。其實裝飾器的語法本身挺簡單的,復雜是因為同時混雜了其它的概念。下面我們一起拋去無關(guān)概念,簡單地理解下Python的裝飾器。

裝飾器的原理

???????在解釋器下跑個裝飾器的例子,直觀地感受一下

# make_bold就是裝飾器,實現(xiàn)方式這里略去

>>> @make_bold
... def get_content():
...     return "hello world"
...
>>> get_content()
"hello world"

???????被make_bold裝飾的get_content,調(diào)用后返回結(jié)果會自動被b標簽包住。怎么做到的呢,簡單4步就能明白了。

1. 函數(shù)是對象

???????我們定義個get_content函數(shù)。這時get_content也是個對象,它能做所有對象的操作。

它有id,有type,有值。

>>> id(get_content)
140090200473112
>>> type(get_content)

>>> get_content

跟其他對象一樣可以被賦值給其它變量。

>>> func_name = get_content
>>> func_name()
"hello world"

它可以當參數(shù)傳遞,也可以當返回值

>>> def foo(bar):
...     print(bar())
...     return bar
...
>>> func = foo(get_content)
hello world
>>> func()
"hello world"
2. 自定義函數(shù)對象

???????我們可以用class來構(gòu)造函數(shù)對象。有成員函數(shù)__call__的就是函數(shù)對象了,函數(shù)對象被調(diào)用時正是調(diào)用的__call__。

class FuncObj(object):
    def __init__(self, name):
        print("Initialize")
        self.name= name

    def __call__(self):
        print("Hi", self.name)

我們來調(diào)用看看。可以看到,函數(shù)對象的使用分兩步:構(gòu)造和調(diào)用(同學們注意了,這是考點)。

>>> fo = FuncObj("python")
    Initialize
>>> fo()
    "Hi python"
3. @是個語法糖

裝飾器的@沒有做什么特別的事,不用它也可以實現(xiàn)一樣的功能,只不過需要更多的代碼。

@make_bold
def get_content():
    return "hello world"

上面的代碼等價于下面的

def get_content():
    return "hello world"
get_content = make_bold(get_content)

make_bold是個函數(shù),要求入?yún)⑹呛瘮?shù)對象,返回值是函數(shù)對象。@的語法糖其實是省去了上面最后一行代碼,使可讀性更好。用了裝飾器后,每次調(diào)用get_content,真正調(diào)用的是make_bold返回的函數(shù)對象。

4. 用類實現(xiàn)裝飾器

入?yún)⑹呛瘮?shù)對象,返回是函數(shù)對象,如果第2步里的類的構(gòu)造函數(shù)改成入?yún)⑹莻€函數(shù)對象,不就正好符合要求嗎?我們來試試實現(xiàn)make_bold。

class make_bold(object):
    def __init__(self, func):
        print("Initialize")
        self.func = func

    def __call__(self):
        print("Call")
        return "{}".format(self.func())

大功告成,看看能不能用。

>>> @make_bold
... def get_content():
...     return "hello world"
...
Initialize
>>> get_content()
Call
"hello world"

成功實現(xiàn)裝飾器!是不是很簡單?
這里分析一下之前強調(diào)的構(gòu)造和調(diào)用兩個過程。我們?nèi)サ鬇語法糖好理解一些。

# 構(gòu)造,使用裝飾器時構(gòu)造函數(shù)對象,調(diào)用了__init__
>>> get_content = make_bold(get_content)
Initialize

# 調(diào)用,實際上直接調(diào)用的是make_bold構(gòu)造出來的函數(shù)對象
>>> get_content()
Call
"hello world"

到這里就徹底清楚了,完結(jié)撒花,可以關(guān)掉網(wǎng)頁了~~~(如果只是想知道裝飾器原理的話)


函數(shù)版裝飾器

閱讀源碼時,經(jīng)常見到用嵌套函數(shù)實現(xiàn)的裝飾器,怎么理解?同樣僅需4步。

1. def的函數(shù)對象初始化

用class實現(xiàn)的函數(shù)對象很容易看到什么時候構(gòu)造的,那def定義的函數(shù)對象什么時候構(gòu)造的呢?

# 這里的全局變量刪去了無關(guān)的內(nèi)容
>>> globals()
{}
>>> def func():
...     pass
...
>>> globals()
{"func": }

不像一些編譯型語言,程序在啟動時函數(shù)已經(jīng)構(gòu)造那好了。上面的例子可以看到,執(zhí)行到def會才構(gòu)造出一個函數(shù)對象,并賦值給變量make_bold。

這段代碼和下面的代碼效果是很像的。

class NoName(object):
    def __call__(self):
        pass
func = NoName()
2. 嵌套函數(shù)

Python的函數(shù)可以嵌套定義。

def outer():
    print("Before def:", locals())
    def inner():
        pass
    print("After def:", locals())
    return inner
    
#inner是在outer內(nèi)定義的,所以算outer的局部變量。
#執(zhí)行到def inner時函數(shù)對象才創(chuàng)建,因此每次調(diào)用outer都會創(chuàng)建一個新的inner。
#下面可以看出,每次返回的inner是不同的。

>>> outer()
Before def: {}
After def: {"inner": .inner at 0x7f0b18fa0048>}
.inner at 0x7f0b18fa0048>
>>> outer()
Before def: {}
After def: {"inner": .inner at 0x7f0b18fa00d0>}
.inner at 0x7f0b18fa00d0>
3. 閉包

嵌套函數(shù)有什么特別之處?因為有閉包。

def outer():
    msg = "hello world"
    def inner():
        print(msg)
    return inner

下面的試驗表明,inner可以訪問到outer的局部變量msg。

>>> func = outer()
>>> func()
hello world

閉包有2個特點
inner能訪問outer及其祖先函數(shù)的命名空間內(nèi)的變量(局部變量,函數(shù)參數(shù))。
調(diào)用outer已經(jīng)返回了,但是它的命名空間被返回的inner對象引用,所以還不會被回收。
這部分想深入可以去了解Python的LEGB規(guī)則。

4. 用函數(shù)實現(xiàn)裝飾器

裝飾器要求入?yún)⑹呛瘮?shù)對象,返回值是函數(shù)對象,嵌套函數(shù)完全能勝任。

def make_bold(func):
    print("Initialize")
    def wrapper():
        print("Call")
        return "{}".format(func())
    return wrapper

用法跟類實現(xiàn)的裝飾器一樣??梢匀サ鬇語法糖分析下構(gòu)造和調(diào)用的時機。

>>> @make_bold
... def get_content():
...     return "hello world"
...
Initialize
>>> get_content()
Call
"hello world"

因為返回的wrapper還在引用著,所以存在于make_bold命名空間的func不會消失。make_bold可以裝飾多個函數(shù),wrapper不會調(diào)用混淆,因為每次調(diào)用make_bold,都會有創(chuàng)建新的命名空間和新的wrapper。
到此函數(shù)實現(xiàn)裝飾器也理清楚了,完結(jié)撒花,可以關(guān)掉網(wǎng)頁了~~~(后面是使用裝飾的常見問題)

常見問題 1. 怎么實現(xiàn)帶參數(shù)的裝飾器?

帶參數(shù)的裝飾器,有時會異常的好用。我們看個例子。

    >>> @make_header(2)
    ... def get_content():
    ...     return "hello world"
    ...
    >>> get_content()
    "

hello world

" #怎么做到的呢?其實這跟裝飾器語法沒什么關(guān)系。去掉@語法糖會變得很容易理解。 @make_header(2) def get_content(): return "hello world" # 等價于 def get_content(): return "hello world" unnamed_decorator = make_header(2) get_content = unnamed_decorator(get_content)

上面代碼中的unnamed_decorator才是真正的裝飾器,make_header是個普通的函數(shù),它的返回值是裝飾器。

來看一下實現(xiàn)的代碼。

def make_header(level):
    print("Create decorator")

    # 這部分跟通常的裝飾器一樣,只是wrapper通過閉包訪問了變量level
    def decorator(func):
        print("Initialize")
        def wrapper():
            print("Call")
            return "{1}".format(level, func())
        return wrapper

    # make_header返回裝飾器
    return decorator

看了實現(xiàn)代碼,裝飾器的構(gòu)造和調(diào)用的時序已經(jīng)很清楚了。

>>> @make_header(2)
... def get_content():
...     return "hello world"
...
Create decorator
Initialize
>>> get_content()
Call
"

hello world

"
2. 如何裝飾有參數(shù)的函數(shù)?

為了有條理地理解裝飾器,之前例子里的被裝飾函數(shù)有意設計成無參的。我們來看個例子。

@make_bold
def get_login_tip(name):
    return "Welcome back, {}".format(name)

最直接的想法是把get_login_tip的參數(shù)透傳下去。

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

    def __call__(self, name):
        return "{}".format(self.func(name))

???????如果被裝飾的函數(shù)參數(shù)是明確固定的,這么寫是沒有問題的。但是make_bold明顯不是這種場景。它既需要裝飾沒有參數(shù)的get_content,又需要裝飾有參數(shù)的get_login_tip。這時候就需要可變參數(shù)了。

class make_bold(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        return "{}".format(self.func(*args, **kwargs))

???????當裝飾器不關(guān)心被裝飾函數(shù)的參數(shù),或是被裝飾函數(shù)的參數(shù)多種多樣的時候,可變參數(shù)非常合適??勺儏?shù)不屬于裝飾器的語法內(nèi)容,這里就不深入探討了。

3. 一個函數(shù)能否被多個裝飾器裝飾?

下面這么寫合法嗎?

@make_italic
@make_bold
def get_content():
    return "hello world"

合法。上面的的代碼和下面等價,留意一下裝飾的順序。

def get_content():
    return "hello world"
get_content = make_bold(get_content) # 先裝飾離函數(shù)定義近的
get_content = make_italic(get_content)
4. functools.wraps有什么用?

???????Python的裝飾器倍感貼心的地方是對調(diào)用方透明。調(diào)用方完全不知道也不需要知道調(diào)用的函數(shù)被裝飾了。這樣我們就能在調(diào)用方的代碼完全不改動的前提下,給函數(shù)patch功能。
???????為了對調(diào)用方透明,裝飾器返回的對象要偽裝成被裝飾的函數(shù)。偽裝得越像,對調(diào)用方來說差異越小。有時光偽裝函數(shù)名和參數(shù)是不夠的,因為Python的函數(shù)對象有一些元信息調(diào)用方可能讀取了。為了連這些元信息也偽裝上,functools.wraps出場了。它能用于把被調(diào)用函數(shù)的__module__,__name__,__qualname__,__doc__,__annotations__賦值給裝飾器返回的函數(shù)對象。

import functools
def make_bold(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return "{}".format(func(*args, **kwargs))
    return wrapper

對比一下效果。

>>> @make_bold
... def get_content():
...     """Return page content"""
...     return "hello world"
>>>
# 不用functools.wraps的結(jié)果
>>> get_content.__name__
"wrapper"
>>> get_content.__doc__
>>>
# 用functools.wraps的結(jié)果
>>> get_content.__name__
"get_content"
>>> get_content.__doc__
"Return page content"

???????實現(xiàn)裝飾器時往往不知道調(diào)用方會怎么用,所以養(yǎng)成好習慣加上functools.wraps吧。

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/40872.html

相關(guān)文章

  • python奇遇記:深入理解裝飾

    摘要:可見裝飾器改變了函數(shù)的功能。裝飾器除了改變函數(shù)功能之外還有一個特性是,函數(shù)裝飾器在導入模塊時立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時運行。 什么是裝飾器 裝飾器是什么,簡單來說,裝飾器可以改變一個函數(shù)的行為,比如原本有一個函數(shù)用來計算菲波那切數(shù)列,我們給這個函數(shù)加個計算執(zhí)行時間的裝飾器,這樣原來的函數(shù)不僅能夠計算菲波那切數(shù)列,而且還可以輸出計算花費了多少時間。 在Python中,有幾個很...

    lemon 評論0 收藏0
  • Python知識點:理解和使用裝飾 @decorator

    摘要:使用類裝飾器,優(yōu)點是靈活性大,高內(nèi)聚,封裝性。不過不用擔心,有,本身也是一個裝飾器,它的作用就是把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息。 showImg(https://segmentfault.com/img/bVbrFWb?w=742&h=484);Python的裝飾器(decorator)是一個很棒的機制,也是熟練運用Python的必殺技之一。...

    cyqian 評論0 收藏0
  • python裝飾入門小結(jié)

    摘要:使用一年多了,一直知道有個裝飾器,很好用,試圖理解過,可能由于資料找的不好,自己的悟性太差,一直沒有搞清楚,今天查了一些資料,算是理解了,現(xiàn)在簡單記錄下。 使用python一年多了,一直知道python有個裝飾器,很好用,試圖理解過,可能由于資料找的不好,自己的悟性太差,一直沒有搞清楚,今天查了一些資料,算是理解了,現(xiàn)在簡單記錄下。python的裝飾器本身的功能是在不改變已有函數(shù)本身的...

    SunZhaopeng 評論0 收藏0
  • Python】一文弄懂python裝飾(附源碼例子)

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

    liuchengxu 評論0 收藏0
  • Python: 會打扮的裝飾

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

    blastz 評論0 收藏0

發(fā)表評論

0條評論

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