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

資訊專欄INFORMATION COLUMN

Python學(xué)習(xí)之路26-函數(shù)裝飾器和閉包

sunny5541 / 2473人閱讀

摘要:初步認(rèn)識(shí)裝飾器函數(shù)裝飾器用于在源代碼中標(biāo)記函數(shù),以某種方式增強(qiáng)函數(shù)的行為。函數(shù)裝飾器在導(dǎo)入模塊時(shí)立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時(shí)運(yùn)行。只有涉及嵌套函數(shù)時(shí)才有閉包問題。如果想保留函數(shù)原本的屬性,可以使用標(biāo)準(zhǔn)庫中的裝飾器。

《流暢的Python》筆記

本篇將從最簡(jiǎn)單的裝飾器開始,逐漸深入到閉包的概念,然后實(shí)現(xiàn)參數(shù)化裝飾器,最后介紹標(biāo)準(zhǔn)庫中常用的裝飾器。

1. 初步認(rèn)識(shí)裝飾器

函數(shù)裝飾器用于在源代碼中“標(biāo)記”函數(shù),以某種方式增強(qiáng)函數(shù)的行為。裝飾器就是函數(shù),或者說是可調(diào)用對(duì)象,它以另一個(gè)函數(shù)為參數(shù),最后返回一個(gè)函數(shù),但這個(gè)返回的函數(shù)并不一定是原函數(shù)。

1.1 裝飾器基礎(chǔ)用法

以下是裝飾器最基本的用法:

# 代碼1
#裝飾器用法
@decorate
def target(): pass

# 上述代碼等價(jià)于以下代碼
def target(): pass
target = decorate(target)

即,最終的target函數(shù)是由decorate(target)返回的函數(shù)。下面這個(gè)例子說明了這一點(diǎn):

# 代碼2
def deco(func):
    def inner():
        print("running inner()")
    return inner

@deco
def target():
    print("running target()")

target()
print(target)

# 結(jié)果
running inner() # 輸出的是裝飾器內(nèi)部定義的函數(shù)的調(diào)用結(jié)果
.inner at 0x000001AF32547D90>

從上面可看出,裝飾器的一大特性是能把被裝飾的函數(shù)替換成其他函數(shù)。但嚴(yán)格說來,裝飾器只是語法糖(語法糖:在編程語言中添加某種語法,但這種語法對(duì)語言的功能沒有影響,只是更方便程序員使用)。

裝飾器還可以疊加。下面是一個(gè)說明,具體例子見后面章節(jié):

# 代碼3
@d1
@d2
def f(): pass

#上述代碼等價(jià)于以下代碼:
def f(): pass
f = d1(d2(f))
1.2 Python何時(shí)執(zhí)行裝飾器

裝飾器的另一個(gè)關(guān)鍵特性是,它在被裝飾的函數(shù)定義后立即運(yùn)行,這通常是在導(dǎo)入時(shí),即Python加載模塊時(shí):

# 代碼4
registry = []

def register(func):
    print("running register(%s)" % func)
    registry.append(func)
    return func

@register
def f1():
    print("running f1()")

def f2():
    print("running f2()")

if __name__ == "__main__":
    print("running in main")
    print("registry ->", registry)
    f1()
    f2()

# 結(jié)果
running register()
running in main # 進(jìn)入到主程序
registry -> []
running f1()
running f2()

裝飾器register在加載模塊時(shí)就對(duì)f1()進(jìn)行了注冊(cè),所以當(dāng)運(yùn)行主程序時(shí),列表registry并不為空。

函數(shù)裝飾器在導(dǎo)入模塊時(shí)立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時(shí)運(yùn)行。這突出了Python程序員常說的導(dǎo)入時(shí)運(yùn)行時(shí)之間的區(qū)別。

裝飾器在真實(shí)代碼中的使用方式與代碼4中有所不同:

裝飾器和被裝飾函數(shù)一般不在一個(gè)模塊中,通常裝飾器定義在一個(gè)模塊中,然后應(yīng)用到其他模塊中的函數(shù)上;

大多數(shù)裝飾器會(huì)在內(nèi)部定義一個(gè)函數(shù),然后將其返回。

代碼4中的裝飾器原封不動(dòng)地返回了傳入的函數(shù)。這種裝飾器并不是沒有用,正如代碼4中的裝飾器的名字一樣,這類裝飾器常充當(dāng)了注冊(cè)器,很多Web框架就使用了這種方法。下一小節(jié)也是該類裝飾器的一個(gè)例子。

1.3 使用裝飾器改進(jìn)策略模式

上一篇中我們用Python函數(shù)改進(jìn)了傳統(tǒng)的策略模式,其中,我們定義了一個(gè)promos列表來記錄有哪些具體策略,當(dāng)時(shí)的做法是用globals()函數(shù)來獲取具體的策略函數(shù),現(xiàn)在我們用裝飾器來改進(jìn)這一做法:

# 代碼5,對(duì)之前的代碼進(jìn)行了簡(jiǎn)略
promos = []

def promotion(promo_func): # 只充當(dāng)了注冊(cè)器
    promos.append(promo_func)
    return promo_func

@promotion
def fidelity(order): pass  

@promotion
def bulk_item(order): pass

@promotion
def large_order(order): pass

def best_promo(order):
    return max(promo(order) for promo in promos)

該方案相比之前的方案,有以下三個(gè)優(yōu)點(diǎn):

促銷策略函數(shù)無需使用特殊名字,即不用再以_promo結(jié)尾

@promotion裝飾器突出了被裝飾函數(shù)的作用,還便于臨時(shí)禁用某個(gè)促銷策略(只需將裝飾器注釋掉)

促銷策略函數(shù)在任何地方定義都行,只要加上裝飾器即可。

2. 閉包

正如前文所說,多數(shù)裝飾器會(huì)在內(nèi)部定義函數(shù),并將其返回,已替換掉傳入的函數(shù)。這個(gè)機(jī)制的實(shí)現(xiàn)就要靠閉包,但在理解閉包之前,先來看看Python中的變量作用域。

2.1 變量作用域規(guī)則

通過下述例子來解釋局部變量和全局變量:

# 代碼6
>>> def f1(a):
...     print(a)
...     print(b)
    
>>> f1(3)
3
Traceback (most recent call last):
  -- snip --
NameError: name "b" is not defined

當(dāng)代碼運(yùn)行到print(a)時(shí),Python查找變量a,發(fā)現(xiàn)變量a存在于局部作用域中,于是順利執(zhí)行;當(dāng)運(yùn)行到print(b)時(shí),python查找變量b,發(fā)現(xiàn)局部作用域中并沒有變量b,便接著查找全局作用域,發(fā)現(xiàn)也沒有變量b,最終報(bào)錯(cuò)。正確的調(diào)用方式相信大家也知道,就是在調(diào)用f1(3)之前給變量b賦值。

我們?cè)倏慈缦麓a:

# 代碼7
>>> b = 6
>>> def f2(a):
...     print(a)
...     print(b)
...     b = 9
    
>>> f2(3)
3
Traceback (most recent call last):
  -- snip --
UnboundLocalError: local variable "b" referenced before assignment

按理說不應(yīng)該報(bào)錯(cuò),并且b的值應(yīng)該打印為6,但結(jié)果卻不是這樣。

事實(shí)是:變量b本來是全局變量,但由于在f2()中我們?yōu)樽兞?b>b賦了值,于是Python在局部作用域中也注冊(cè)了一個(gè)名為b的變量(全局變量b依然存在,有編程基礎(chǔ)的同學(xué)應(yīng)該知道,這叫做“覆蓋”)。當(dāng)Python執(zhí)行到print(b)語句時(shí),Python先搜索局部作用域,發(fā)現(xiàn)其中有變量b,但是b此時(shí)還沒有被賦值(全局變量b被覆蓋,而局部變量b的賦值語句在該句后面),于是Python報(bào)錯(cuò)。

如果不想代碼7報(bào)錯(cuò),則需要使用global語句,將變量b聲明為全局變量:

# 代碼8
>>> b = 6
>>> def f2(a):
...     global b
...     -- snip --
2.2 閉包的概念

現(xiàn)在開始真正接觸閉包。閉包指延伸了作用域的函數(shù),它包含函數(shù)定義體中引用,但不在定義體中定義的非全局變量,即這類函數(shù)能訪問定義體之外的非全局變量。只有涉及嵌套函數(shù)時(shí)才有閉包問題。

下面用一個(gè)例子來說明閉包以及非全局變量。定義一個(gè)計(jì)算某商品一段時(shí)間內(nèi)均價(jià)的函數(shù)avg,它的表現(xiàn)如下:

# 代碼9
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0

假定商品價(jià)格每天都在變化,因此需要一個(gè)變量來保存這些值。如果用類的思想,我們可以定義一個(gè)可調(diào)用對(duì)象,把這些值存到內(nèi)部屬性中,然后實(shí)現(xiàn)__call__方法,讓其表現(xiàn)得像函數(shù);但如果按裝飾器的思想,可以定義一個(gè)如下的嵌套函數(shù):

# 代碼10
def make_averager():
    series = []

    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total / len(series)

    return averager

然后以如下方式使用這個(gè)函數(shù):

# 代碼11
>>> avg = make_averager()
>>> avg(10)
10.0
-- snip --

不知道大家剛接觸這個(gè)內(nèi)部的averager()函數(shù)時(shí)有沒有疑惑:代碼11中,當(dāng)執(zhí)行avg(10)時(shí),它是到哪里去找的變量series?series是函數(shù)make_averager()的局部變量,當(dāng)make_averager()返回了averager()后,它的局部作用域就消失了,所以按理說series也應(yīng)該跟著消失,并且上述代碼應(yīng)該報(bào)錯(cuò)才對(duì)。

事實(shí)上,在averager函數(shù)中,series自由變量(free variable),即未在局部作用域中綁定的變量。這里,自由變量series和內(nèi)部函數(shù)averager共同組成了閉包,參考下圖:

實(shí)際上,Python在averager__code__屬性中保存了局部變量和自由變量的名稱,在__closure__屬性中保存了自由變量的值:

# 代碼12,注意這些變量的單詞含義,一目了然
>>> avg.__code__.co_varnames  # co_varnames保存局部變量的名稱
("new_value", "total")
>>> avg.__code__.co_freevars # co_freevars保存自由變量的名稱
("series",)
>>> avg.__closure__ # 單詞closure就是閉包的意思
# __closure__是一個(gè)cell對(duì)象列表,其中的元素和co_freevars元組一一對(duì)應(yīng)
(,)
>>> avg.__closure__[0].cell_contents 
[10, 11, 12] # cell對(duì)象的cell_contents屬性才是真正保存自由變量的值的地方

綜上:閉包是一種函數(shù),它會(huì)保存定義函數(shù)時(shí)存在的自由變量的綁定,這樣調(diào)用函數(shù)時(shí),雖然外層函數(shù)的局部作用域不可用了,但仍能使用那些綁定。

注意:只有嵌套在其他函數(shù)中的函數(shù)才可能需要處理不在全局作用域中的外部變量。

2.3 nonlocal聲明

代碼10中的make_averager函數(shù)并不高效,因?yàn)槿绻挥?jì)算均值的話,其實(shí)不用保存每次的價(jià)格,我們可按如下方式改寫代碼10

# 代碼13
def make_averager():
    count = 0
    total = 0

    def averager(new_value):
        count += 1
        total += new_value
        return total / count

    return averager

但此時(shí)直接運(yùn)行代碼11的話,則會(huì)報(bào)代碼7中的錯(cuò)誤:UnboundLocalError。

問題在于:由于count是不可變類型,在執(zhí)行count += 1時(shí),該語句等價(jià)于count = count + 1,而這就成了賦值語句,count不再是自由變量,而變成了averager的局部變量。total也是一樣的情況。而在之前的代碼10中沒有這個(gè)問題,因?yàn)?b>series是個(gè)可變類型,我們只是調(diào)用series.append,以及把它傳給了sumlen,它并沒有變?yōu)榫植孔兞俊?/p>

對(duì)于不可變類型來說,只能讀取,不能更新,否則會(huì)隱式創(chuàng)建局部變量。為了解決這個(gè)問題,Python3引入了nonlocal聲明。它的作用是把變量顯式標(biāo)記為自由變量:

# 代碼14
def make_averager():
    count = 0
    total = 0

    def averager(new_value):
        nonlocal count, total
        -- snip --
3. 裝飾器

了解了閉包后,現(xiàn)在開始正式使用嵌套函數(shù)來實(shí)現(xiàn)裝飾器。首先來認(rèn)識(shí)標(biāo)準(zhǔn)庫中三個(gè)重要的裝飾器。

3.1 標(biāo)準(zhǔn)庫中的裝飾器 3.1.1 functools.wraps裝飾器

來看一個(gè)簡(jiǎn)單的裝飾器:

# 代碼15
def deco(func):
    def test():
        func()
    return test

@deco
def Test():
    """This is a test"""
    print("This is a test")

print(Test.__name__)
print(Test.__doc__)

# 結(jié)果
test
None

我們想讓裝飾器來自動(dòng)幫我們做一些額外的操作,但像改變函數(shù)屬性這樣的操作并不一定是我們想要的:從上面可以看出,Test現(xiàn)在指向了內(nèi)部函數(shù)test,Test自身的屬性被遮蓋。如果想保留函數(shù)原本的屬性,可以使用標(biāo)準(zhǔn)庫中的functools.wraps裝飾器。下面以一個(gè)更復(fù)雜的裝飾器為例,它會(huì)在每次調(diào)用被裝飾函數(shù)時(shí)計(jì)時(shí),并將經(jīng)過的時(shí)間,傳入的參數(shù)和調(diào)用的結(jié)果打印出來:

# 代碼16
# clockdeco.py
import time, functools

def clock(func): # 兩層嵌套
    @functools.wraps(func)  # 綁定屬性
    def clocked(*args, **kwargs):
        t0 = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - t0
        name = func.__name__
        arg_lst = [] # 參數(shù)列表
        if args:
            arg_lst.append(", ".join(repr(arg) for arg in args))
        if kwargs:
            pairs = ["%s=%r" % (k, w) for k, w in sorted(kwargs.items())]
            arg_lst.append(", ".join(pairs))
        arg_str = ", ".join(arg_lst)
        print("[%0.8fs] %s(%s) -> %r" % (elapsed, name, arg_str, result))
        return result
    return clocked

它的使用將和下一個(gè)裝飾器一起展示。

3.1.2 functools.lru_cache裝飾器

functools.lru_cache實(shí)現(xiàn)了備忘(memoization)功能,這是一項(xiàng)優(yōu)化技術(shù),他把耗時(shí)的函數(shù)的結(jié)果保存起來,避免傳入相同參數(shù)時(shí)重復(fù)計(jì)算。以斐波那契函數(shù)為例,我們知道以遞歸形式實(shí)現(xiàn)的斐波那契函數(shù)會(huì)出現(xiàn)很多重復(fù)計(jì)算,此時(shí),就可以使用這個(gè)裝飾器。以下代碼是沒使用該裝飾器時(shí)的運(yùn)行情況:

# 代碼17
from clockdeco import clock

@clock
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 2) + fibonacci(n - 1)

if __name__ == "__main__":
    print(fibonacci.__name__)
    print(fibonacci.__doc__)
    print(fibonacci(6))

# 結(jié)果:
fibonacci  # fibonacci原本的屬性得到了保留
None
[0.00000000s] fibonacci(0) -> 0
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(2) -> 1
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(0) -> 0
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(2) -> 1
[0.00000000s] fibonacci(3) -> 2
[0.00000000s] fibonacci(4) -> 3
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(0) -> 0
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(2) -> 1
[0.00000000s] fibonacci(3) -> 2
[0.00000000s] fibonacci(0) -> 0
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(2) -> 1
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(0) -> 0
[0.00000000s] fibonacci(1) -> 1
[0.00049996s] fibonacci(2) -> 1
[0.00049996s] fibonacci(3) -> 2
[0.00049996s] fibonacci(4) -> 3
[0.00049996s] fibonacci(5) -> 5
[0.00049996s] fibonacci(6) -> 8
8

可以看出,fibonacci(1)調(diào)用了8次,下面我們用functools.lru_cache來改進(jìn)上述代碼:

# 代碼18
import functools
from clockdeco import clock

@functools.lru_cache()  # 注意此處有個(gè)括號(hào)!該裝飾器就收參數(shù)!不能?。?@clock
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 2) + fibonacci(n - 1)

if __name__ == "__main__":
    print(fibonacci(6))
    
# 結(jié)果:
[0.00000000s] fibonacci(0) -> 0
[0.00000000s] fibonacci(1) -> 1
[0.00000000s] fibonacci(2) -> 1
[0.00000000s] fibonacci(3) -> 2
[0.00000000s] fibonacci(4) -> 3
[0.00000000s] fibonacci(5) -> 5
[0.00000000s] fibonacci(6) -> 8
8

functools.lru_cache裝飾器可以接受參數(shù),并且此代碼還疊放了裝飾器。

lru_cache有兩個(gè)參數(shù):functools.lru_cache(maxsize=128, typed=False)

maxsize指定存儲(chǔ)多少個(gè)調(diào)用的結(jié)果,該參數(shù)最好是2的冪。當(dāng)緩存滿后,根據(jù)LRU算法替換緩存中的內(nèi)容,這也是為什么這個(gè)函數(shù)叫lru_cache。

type如果設(shè)置為True,它將把不同參數(shù)類型下得到的結(jié)果分開保存,即把通常認(rèn)為相等的浮點(diǎn)數(shù)和整數(shù)參數(shù)分開(比如區(qū)分1和1.0)。

lru_cache使用字典存儲(chǔ)結(jié)果,字典的鍵是傳入的參數(shù),所以被lru_cache裝飾的函數(shù)的所有參數(shù)都必須是可散列的!

3.1.3 functools.singledispatch裝飾器

我們知道,C++支持函數(shù)重載,同名函數(shù)可以根據(jù)參數(shù)類型的不同而調(diào)用相應(yīng)的函數(shù)。以Python代碼為例,我們希望下面這個(gè)函數(shù)表現(xiàn)出如下行為:

# 代碼19
def myprint(obj):
    return "Hello~~~"

# 以下是我們希望它擁有的行為:
>>> myprint(1)
Hello~~~
>>> myprint([])
Hello~~~
>>> myprint("hello") # 即,當(dāng)我們傳入特定類型的參數(shù)時(shí),函數(shù)返回特定的結(jié)果
This is a str

單憑這一個(gè)myprint還無法實(shí)現(xiàn)上述要求,因?yàn)镻ython不支持方法或函數(shù)的重載。為了實(shí)現(xiàn)類似的功能,一種常見的做法是將函數(shù)變?yōu)橐粋€(gè)分派函數(shù),使用一串if/elif/elif來判斷參數(shù)類型,再調(diào)用專門的函數(shù)(如myprint_str),但這種方式不利于代碼的擴(kuò)展和維護(hù),還顯得沒有B格。。。

為解決這個(gè)問題,從Python3.4開始,可以使用functools.singledispath裝飾器,把整體方案拆分成多個(gè)模塊,甚至可以為無法修改的類提供專門的函數(shù)。被@singledispatch裝飾的函數(shù)會(huì)變成泛函數(shù)(generic function),它會(huì)根據(jù)第一個(gè)參數(shù)的不同而調(diào)用響應(yīng)的專門函數(shù),具體用法如下:

# 代碼20
from functools import singledispatch
import numbers

@singledispatch
def myprint(obj):
    return "Hello~~~"

# 可以疊放多個(gè)register,讓同一函數(shù)支持不同類型
@myprint.register(str)
# 注冊(cè)的專門函數(shù)最好處理抽象基類,而不是具體實(shí)現(xiàn),這樣代碼支持的兼容類型更廣泛
@myprint.register(numbers.Integral) 
def _(text): # 專門函數(shù)的名稱無所謂,使用 _ 可以避免起名字的麻煩
    return "Special types"

對(duì)泛函數(shù)的補(bǔ)充:根據(jù)參數(shù)類型的不同,以不同方式執(zhí)行相同操作的一組函數(shù)。如果依據(jù)是第一個(gè)參數(shù),則是單分派;如果依據(jù)是多個(gè)參數(shù),則是多分派。

3.2 參數(shù)化裝飾器 3.2.1 簡(jiǎn)單版參數(shù)化裝飾器

從上面諸多例子我們可以看到兩大類裝飾器:不帶參數(shù)的裝飾器(調(diào)用時(shí)最后沒有括號(hào))和帶參數(shù)的裝飾器(帶括號(hào))。Python將被裝飾的函數(shù)作為第一個(gè)參數(shù)傳給了裝飾器函數(shù),那裝飾器函數(shù)如何接受其他參數(shù)呢?做法是:創(chuàng)建一個(gè)裝飾器工廠函數(shù),在這個(gè)工廠函數(shù)內(nèi)部再定義其它函數(shù)作為真正的裝飾器。工廠函數(shù)代為接受參數(shù),這些參數(shù)作為自由變量供裝飾器使用。然后工廠函數(shù)返回裝飾器,裝飾器再應(yīng)用到被裝飾函數(shù)上。

我們把1.2中代碼4@register裝飾器改為帶參數(shù)的版本,以active參數(shù)來指示裝飾器是否注冊(cè)某函數(shù)(雖然這么做有點(diǎn)多余)。這里只給出@register裝飾器的實(shí)現(xiàn),其余代碼參考代碼4

# 代碼21
registry = set()

def register(active=True):
    def decorate(func): # 變量active對(duì)于decorate函數(shù)來說是自由變量
        print("running register(active=%s)->decorate(%s)" % (active, func))
        if active: 
            registry.add(func)
        else:
            registry.discard(func)
        return func
    return decorate

# 用法
@register(active=False) # 即使不傳參數(shù)也要作為函數(shù)調(diào)用@register()
def f():pass

# 上述用法相當(dāng)于如下代碼:
# register(active=False)(f)
3.2.2 多層嵌套版參數(shù)化裝飾器

參數(shù)化裝飾器通常會(huì)把被裝飾函數(shù)替換掉,而且結(jié)構(gòu)上需要多一層嵌套。下面以3.1.1中代碼16里的@clock裝飾器為例,讓它按用戶要求的格式輸出數(shù)據(jù)。為了簡(jiǎn)便,不調(diào)用functools.wraps裝飾器:

# 代碼22
import time

DEFAULT_FMT = "[{elapsed:0.8f}s] {name}({args}) -> {result}"

def clock(fmt=DEFAULT_FMT):   # 裝飾器工廠,fmt是裝飾器的參數(shù)
    def decorate(func):       # 裝飾器
        def clocked(*_args):  # 最終的函數(shù)
            t0 = time.time()
            _result = func(*_args)
            elapsed = time.time() - t0
            name = func.__name__
            args = ", ".join(repr(arg) for arg in _args)
            result = repr(_result)
            print(fmt.format(**locals())) #locals()函數(shù)以字典形式返回clocked的局部變量
            return _result
        return clocked
    return decorate

可以得到如下結(jié)論:裝飾器函數(shù)有且只有一個(gè)參數(shù),即被裝飾器的函數(shù);如果裝飾器要接受其他參數(shù),請(qǐng)?jiān)谠镜难b飾器外再套一層函數(shù)(工廠函數(shù)),由它來接受其余參數(shù);而你最終使用的函數(shù)應(yīng)該定義在裝飾器函數(shù)中,且它的參數(shù)列表應(yīng)該和被裝飾的函數(shù)一致。

4. 總結(jié)

本篇首先介紹了最簡(jiǎn)單裝飾器如何定義和使用,介紹了裝飾器在什么時(shí)候被執(zhí)行,以及用最簡(jiǎn)單的裝飾器改造了上一篇的策略模式;隨后更進(jìn)一步,介紹了與閉包相關(guān)的概念,包括變量作用域,閉包和nonlocal聲明;最后介紹了更復(fù)雜的裝飾器,包括標(biāo)準(zhǔn)庫中的裝飾器的用法,以及如何定義帶參數(shù)的裝飾器。

但上述對(duì)裝飾器的描述都是基本的, 更復(fù)雜、工業(yè)級(jí)的裝飾器還需要更深入的學(xué)習(xí)。

迎大家關(guān)注我的微信公眾號(hào)"代碼港" & 個(gè)人網(wǎng)站 www.vpointer.net ~

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

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

相關(guān)文章

  • Python中的函數(shù)裝飾器和閉包

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

    caozhijian 評(píng)論0 收藏0
  • 流暢的python讀書筆記-第七章-函數(shù)裝飾器和閉包

    摘要:函數(shù)裝飾器和閉包嚴(yán)格來說,裝飾器只是語法糖。何時(shí)執(zhí)行裝飾器它們?cè)诒谎b飾的函數(shù)定義之后立即運(yùn)行。裝飾器突出了被裝飾的函數(shù)的作用,還便于臨時(shí)禁用某個(gè)促銷策略只需把裝飾器注釋掉。 函數(shù)裝飾器和閉包 嚴(yán)格來說,裝飾器只是語法糖。如前所示,裝飾器可以像常規(guī)的可調(diào)用對(duì)象那樣調(diào)用,其參數(shù)是另一個(gè)函數(shù)。有時(shí),這樣做更方便,尤其是做元編程(在運(yùn)行時(shí)改變程序的行為)時(shí)。 Python何時(shí)執(zhí)行裝飾器 它們?cè)?..

    Hydrogen 評(píng)論0 收藏0
  • Python_裝飾器和生成器

    摘要:迭代器迭代是訪問集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對(duì)象,迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束,迭代器只往前不會(huì)往后退。生成器特點(diǎn)保存了一套生成數(shù)值的算法。 迭代器 迭代是訪問集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對(duì)象,迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束,迭代器只往前不會(huì)往后退。 可迭代對(duì)象 以直接...

    sugarmo 評(píng)論0 收藏0
  • Python學(xué)習(xí)之路8.2-對(duì)Python類的補(bǔ)充

    摘要:本章主要是對(duì)上一章類的補(bǔ)充。對(duì)于多態(tài)的補(bǔ)充子類可以被看成是父類的類型,但父類不能被看成是子類的類型。仍然以類為例,動(dòng)物里有哺乳動(dòng)物,卵生動(dòng)物,有能飛的動(dòng)物和不能飛的動(dòng)物,這是兩種大的分類方式。一般在中,以為結(jié)尾類的都作為接口。 《Python編程:從入門到實(shí)踐》筆記。本章主要是對(duì)上一章Python類的補(bǔ)充。 1. 從一個(gè)類派生出所有類 上一篇文章說道Python類的定義與繼承一般是如下...

    liukai90 評(píng)論0 收藏0
  • Python閉包裝飾

    摘要:所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 對(duì)于已經(jīng)對(duì) 閉包 或者 裝飾器有一定概念的,可以直接通過右側(cè)標(biāo)題目錄直接定位到相應(yīng)段落查看所需的內(nèi)容。 什么是裝飾器? 裝飾器(Decorator)相對(duì)簡(jiǎn)單,咱們先介紹它:裝飾器的功能是將被裝飾的函數(shù)當(dāng)作參數(shù)傳遞給與裝飾器對(duì)應(yīng)的函數(shù)(名稱相同的函數(shù)),并返回包裝后的被裝飾的函數(shù),聽起來有點(diǎn)繞,沒關(guān)系,直接看示意圖,...

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

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

0條評(píng)論

閱讀需要支付1元查看
<