欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
干貨分享 | 理解 Python 裝飾器的簡(jiǎn)單方法


本文作者:Light Xue

本文出處:http://www.lightxue.com/understand-python-decorator-the-easy-way


Python有大量強大又貼心的特性,如果要列個(gè)最受歡迎排行榜,那么裝飾器絕對會(huì )在其中。


剛接觸裝飾器,會(huì )覺(jué)得代碼不多卻難以理解。其實(shí)裝飾器的語(yǔ)法本身挺簡(jiǎn)單的,復雜是因為同時(shí)混雜了其它的概念。下面我們一起拋去無(wú)關(guān)概念,簡(jiǎn)單地理解下Python的裝飾器。


裝飾器的原理


在解釋器下跑個(gè)裝飾器的例子,直觀(guān)地感受一下。


# make_bold就是裝飾器,實(shí)現方式這里略去

>>> @make_bold

... def get_content():

...     return 'hello world'

...

>>> get_content()

'<b>hello world</b>'


被make_bold裝飾的get_content,調用后返回結果會(huì )自動(dòng)被b標簽包住。怎么做到的呢,簡(jiǎn)單4步就能明白了。


1. 函數是對象


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


def get_content():

    return 'hello world'


它有id,有type,有值。


>>> id(get_content)

140090200473112

>>> type(get_content)

<class 'function'>

>>> get_content

<function get_content at 0x7f694aa2be18>


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


>>> func_name = get_content

>>> func_name()

'hello world'


它可以當參數傳遞,也可以當返回值


>>> def foo(bar):

...     print(bar())

...     return bar

...

>>> func = foo(get_content)

hello world

>>> func()

'hello world'


2. 自定義函數對象


我們可以用class來(lái)構造函數對象。有成員函數__call__的就是函數對象了,函數對象被調用時(shí)正是調用的__call__。


class FuncObj(object):

    def __init__(self, name):

        print('Initialize')

        self.namename

 

    def __call__(self):

        print('Hi', self.name)


我們來(lái)調用看看??梢钥吹?,函數對象的使用分兩步:構造和調用(同學(xué)們注意了,這是考點(diǎn))。


>>> fo = FuncObj('python')

Initialize

>>> fo()

Hi python


3. @是個(gè)語(yǔ)法糖


裝飾器的@沒(méi)有做什么特別的事,不用它也可以實(shí)現一樣的功能,只不過(guò)需要更多的代碼。


@make_bold

def get_content():

    return 'hello world'

 

# 上面的代碼等價(jià)于下面的

 

def get_content():

    return 'hello world'

get_content = make_bold(get_content)


make_bold是個(gè)函數,要求入參是函數對象,返回值是函數對象。@的語(yǔ)法糖其實(shí)是省去了上面最后一行代碼,使可讀性更好。用了裝飾器后,每次調用get_content,真正調用的是make_bold返回的函數對象。


4. 用類(lèi)實(shí)現裝飾器


入參是函數對象,返回是函數對象,如果第2步里的類(lèi)的構造函數改成入參是個(gè)函數對象,不就正好符合要求嗎?我們來(lái)試試實(shí)現make_bold。


class make_bold(object):

    def __init__(self, func):

        print('Initialize')

        self.func = func

 

    def __call__(self):

        print('Call')

        return '<b>{}</b>'.format(self.func())


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


>>> @make_bold

... def get_content():

...     return 'hello world'

...

Initialize

>>> get_content()

Call

'<b>hello world</b>'


成功實(shí)現裝飾器!是不是很簡(jiǎn)單?


這里分析一下之前強調的構造和調用兩個(gè)過(guò)程。我們去掉@語(yǔ)法糖好理解一些。


# 構造,使用裝飾器時(shí)構造函數對象,調用了__init__

>>> get_content = make_bold(get_content)

Initialize

 

# 調用,實(shí)際上直接調用的是make_bold構造出來(lái)的函數對象

>>> get_content()

Call

'<b>hello world</b>'


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


函數版裝飾器


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


1. def的函數對象初始化


用class實(shí)現的函數對象很容易看到什么時(shí)候構造的,那def定義的函數對象什么時(shí)候構造的呢?


# 這里的全局變量刪去了無(wú)關(guān)的內容

>>> globals()

{}

>>> def func():

...     pass

...

>>> globals()

{'func': <function func at 0x10f5baf28>}


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


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


class NoName(object):

    def __call__(self):

        pass

 

func = NoName()


2. 嵌套函數


Python的函數可以嵌套定義。


def outer():

    print('Before def:', locals())

    def inner():

        pass

    print('After def:', locals())

    return inner


inner是在outer內定義的,所以算outer的局部變量。執行到def inner時(shí)函數對象才創(chuàng )建,因此每次調用outer都會(huì )創(chuàng )建一個(gè)新的inner。下面可以看出,每次返回的inner是不同的。


>>> outer()

Before def{}

After def{'inner': <function outer.<locals>.inner at 0x7f0b18fa0048>}

<function outer.<locals>.inner at 0x7f0b18fa0048>

>>> outer()

Before def{}

After def{'inner': <function outer.<locals>.inner at 0x7f0b18fa00d0>}

<function outer.<locals>.inner at 0x7f0b18fa00d0>


3. 閉包


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


def outer():

    msg = 'hello world'

    def inner():

        print(msg)

    return inner


下面的試驗表明,inner可以訪(fǎng)問(wèn)到outer的局部變量msg。


>>> func = outer()

>>> func()

hello world


閉包有2個(gè)特點(diǎn)


  1. inner能訪(fǎng)問(wèn)outer及其祖先函數的命名空間內的變量(局部變量,函數參數)。

  2. 調用outer已經(jīng)返回了,但是它的命名空間被返回的inner對象引用,所以還不會(huì )被回收。


這部分想深入可以去了解Python的LEGB規則。


4. 用函數實(shí)現裝飾器


裝飾器要求入參是函數對象,返回值是函數對象,嵌套函數完全能勝任。


def make_bold(func):

    print('Initialize')

    def wrapper():

        print('Call')

        return '<b>{}</b>'.format(func())

    return wrapper


用法跟類(lèi)實(shí)現的裝飾器一樣??梢匀サ鬇語(yǔ)法糖分析下構造和調用的時(shí)機。


>>> @make_bold

... def get_content():

...     return 'hello world'

...

Initialize

>>> get_content()

Call

'<b>hello world</b>'


因為返回的wrapper還在引用著(zhù),所以存在于make_bold命名空間的func不會(huì )消失。make_bold可以裝飾多個(gè)函數,wrapper不會(huì )調用混淆,因為每次調用make_bold,都會(huì )有創(chuàng )建新的命名空間和新的wrapper。


到此函數實(shí)現裝飾器也理清楚了,完結撒花,可以關(guān)掉網(wǎng)頁(yè)了~~~(后面是使用裝飾的常見(jiàn)問(wèn)題)


常見(jiàn)問(wèn)題


1. 怎么實(shí)現帶參數的裝飾器?


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


>>> @make_header(2)

... def get_content():

...     return 'hello world'

...

>>> get_content()

'<h2>hello world</h2>'


怎么做到的呢?其實(shí)這跟裝飾器語(yǔ)法沒(méi)什么關(guān)系。去掉@語(yǔ)法糖會(huì )變得很容易理解。


@make_header(2)

def get_content():

    return 'hello world'

 

# 等價(jià)于

 

def get_content():

    return 'hello world'

unnamed_decorator = make_header(2)

get_content = unnamed_decorator(get_content)


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


來(lái)看一下實(shí)現的代碼。


def make_header(level):

    print('Create decorator')

 

    # 這部分跟通常的裝飾器一樣,只是wrapper通過(guò)閉包訪(fǎng)問(wèn)了變量level

    def decorator(func):

        print('Initialize')

        def wrapper():

            print('Call')

            return '<h{0}>{1}</h{0}>'.format(level, func())

        return wrapper

 

    # make_header返回裝飾器

    return decorator


看了實(shí)現代碼,裝飾器的構造和調用的時(shí)序已經(jīng)很清楚了。


>>> @make_header(2)

... def get_content():

...     return 'hello world'

...

Create decorator

Initialize

>>> get_content()

Call

'<h2>hello world</h2>'


2. 如何裝飾有參數的函數?


為了有條理地理解裝飾器,之前例子里的被裝飾函數有意設計成無(wú)參的。我們來(lái)看個(gè)例子。


@make_bold

def get_login_tip(name):

    return 'Welcome back, {}'.format(name)


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


class make_bold(object):

    def __init__(self, func):

        self.func = func

 

    def __call__(self, name):

        return '<b>{}</b>'.format(self.func(name))


如果被裝飾的函數參數是明確固定的,這么寫(xiě)是沒(méi)有問(wèn)題的。但是make_bold明顯不是這種場(chǎng)景。它既需要裝飾沒(méi)有參數的get_content,又需要裝飾有參數的get_login_tip。這時(shí)候就需要可變參數了。


class make_bold(object):

    def __init__(self, func):

        self.func = func

    def __call__(self, *args, **kwargs):

        return '<b>{}</b>'.format(self.func(*args, **kwargs))


當裝飾器不關(guān)心被裝飾函數的參數,或是被裝飾函數的參數多種多樣的時(shí)候,可變參數非常合適??勺儏挡粚儆谘b飾器的語(yǔ)法內容,這里就不深入探討了。


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


下面這么寫(xiě)合法嗎?


@make_italic

@make_bold

def get_content():

    return 'hello world'


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


def get_content():

    return 'hello world'

get_content = make_bold(get_content) # 先裝飾離函數定義近的

get_content = make_italic(get_content)


4. functools.wraps有什么用?


Python的裝飾器倍感貼心的地方是對調用方透明。調用方完全不知道也不需要知道調用的函數被裝飾了。這樣我們就能在調用方的代碼完全不改動(dòng)的前提下,給函數patch功能。


為了對調用方透明,裝飾器返回的對象要偽裝成被裝飾的函數。偽裝得越像,對調用方來(lái)說(shuō)差異越小。有時(shí)光偽裝函數名和參數是不夠的,因為Python的函數對象有一些元信息調用方可能讀取了。為了連這些元信息也偽裝上,functools.wraps出場(chǎng)了。它能用于把被調用函數的__module__,__name__,__qualname__,__doc__,__annotations__賦值給裝飾器返回的函數對象。


import functools

 

def make_bold(func):

    @functools.wraps(func)

    def wrapper(*args, **kwargs):

        return '<b>{}</b>'.format(func(*args, **kwargs))

    return wrapper


對比一下效果。


>>> @make_bold

... def get_content():

...     '''Return page content'''

...     return 'hello world'

 

# 不用functools.wraps的結果

>>> get_content.__name__

'wrapper'

>>> get_content.__doc__

>>>

 

# 用functools.wraps的結果

>>> get_content.__name__

'get_content'

>>> get_content.__doc__

'Return page content'


實(shí)現裝飾器時(shí)往往不知道調用方會(huì )怎么用,所以養成好習慣加上functools.wraps吧。


本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
12步輕松搞定python裝飾器
Python裝飾器的實(shí)現原理
python學(xué)習之裝飾器的理解和使用
【Python之路】特別篇
Python裝飾器
Python裝飾器decorator用法實(shí)例
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久