最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python總結(jié)之閉包和裝飾器

 更新時(shí)間:2022年01月07日 15:11:55   作者:liutt233  
這篇文章主要為大家介紹了python閉包和裝飾器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

一、裝飾器

1. 裝飾器的簡(jiǎn)單介紹

“裝飾器的功能是將被裝飾的函數(shù)當(dāng)作參數(shù)傳遞給與裝飾器對(duì)應(yīng)的函數(shù)(名稱相同的函數(shù)),并返回包裝后的被裝飾的函數(shù)”,聽(tīng)起來(lái)有點(diǎn)繞,沒(méi)關(guān)系,直接看示意圖,其中 a 為與裝飾器 @a 對(duì)應(yīng)的函數(shù), b 為裝飾器修飾的函數(shù),裝飾器@a的作用是:

在這里插入圖片描述

舉個(gè)栗子:

def test(func):
    return func
@test
def afunc():
    print("hello")
afunc()

# hello

上面使用@test來(lái)表示裝飾器,其等同于:afunc = test(afunc),因此裝飾器本質(zhì)上就是個(gè)語(yǔ)法糖,其作用為簡(jiǎn)化代碼,以提高代碼可讀性。

2. 裝飾器的解析過(guò)程

step1. python 解釋器發(fā)現(xiàn)@test,就去調(diào)用與其對(duì)應(yīng)的test函數(shù)

step2. test函數(shù)調(diào)用前要指定一個(gè)參數(shù),傳入的就是@test下面修飾的函數(shù),也就是afunc()

step3. test() 函數(shù)執(zhí)行,調(diào)用 afunc(),afunc() 打印“hello”

二、閉包

在計(jì)算機(jī)科學(xué)中,閉包(Closure)是詞法閉包(Lexical Closure)的簡(jiǎn)稱,是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開(kāi)了創(chuàng)造它的環(huán)境也不例外。所以,有另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。閉包在運(yùn)行時(shí)可以有多個(gè)實(shí)例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實(shí)例。

閉包并不是Python中特有的概念,所有把函數(shù)做為一等公民的語(yǔ)言均有閉包的概念。不過(guò)像Java這樣以class為一等公民的語(yǔ)言中也可以使用閉包,只是它得用類或接口來(lái)實(shí)現(xiàn)。

通過(guò)Python的語(yǔ)言介紹,一個(gè)閉包就是你調(diào)用了一個(gè)函數(shù)A,這個(gè)函數(shù)A返回了一個(gè)函數(shù)B給你。這個(gè)返回的函數(shù)B就叫做閉包。你在調(diào)用函數(shù)A的時(shí)候傳遞的參數(shù)就是自由變量。

舉個(gè)栗子:

def func(name):
    def inner_func(age):
        print 'name:', name, 'age:', age
    return inner_func

bb = func('the5fire')
bb(26)  # >>> name: the5fire age: 26

這里面調(diào)用func的時(shí)候就產(chǎn)生了一個(gè)閉包——inner_func,并且該閉包持有自由變量——name,因此這也意味著,當(dāng)函數(shù)func的生命周期結(jié)束之后,name這個(gè)變量依然存在,因?yàn)樗婚]包引用了,所以不會(huì)被回收。

三、閉包中nonlocal語(yǔ)句的使用

1. 外部變量的引用和改寫

在 python 的函數(shù)內(nèi),可以直接引用外部變量,但不能改寫外部變量。

例如在下面的栗子,

counter中可以正常打印常量count,但無(wú)法改變count;對(duì)list可以執(zhí)行append操作,正常修改。

def cnt(param):
    count = 0
    alist = []
    def counter():
        alist.append(1)
        # count += 1 # UnboundLocalError: local variable 'count' referenced before assignment
        print(param, str(count), alist)
    return counter

test = cnt("test")
test()
# test 0 [1]

2. nolocal的使用及特點(diǎn)

為了解決上述不可變變量的修改問(wèn)題:

python 2 中可以在函數(shù)內(nèi)使用 global 語(yǔ)句,但全局變量在任何語(yǔ)言中都不被提倡,因?yàn)樗茈y控制。python 3 中引入了 nonlocal 語(yǔ)句解決了這個(gè)問(wèn)題。

Nonlocal 與 global 的區(qū)別在于:nonlocal 語(yǔ)句會(huì)去搜尋本地變量與全局變量之間的變量,其會(huì)優(yōu)先尋找層級(jí)關(guān)系與閉包作用域最近的外部變量。

def cnt(param):
    count = 0
    def counter():
        nonlocal count
        count += 1
        print(param, str(count))
    return counter

test = cnt("test")
test()

# test 1

四、閉包與裝飾器

上面已經(jīng)簡(jiǎn)單演示了裝飾器的功能,事實(shí)上,裝飾器就是一種的閉包的應(yīng)用,只不過(guò)其傳遞的(自由變量)是函數(shù):

使用裝飾器的寫法:

def make1(fn):
    def wrapped():
        return "<a>" + fn() + "</a>"
    return wrapped

def make2(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

@make1
@make2
def hello():
    return "hello"

print(hello())
# <a><b>hello</b></a>

顯式使用閉包的寫法:

def make1(fn):
    def wrapped():
        return "<a>" + fn() + "</a>"
    return wrapped

def make2(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

def hello():
    return "hello"

hello = make2(hello)
hello = make1(hello)
print(hello())
# <a><b>hello</b></a>

多個(gè)裝飾器裝飾一個(gè)函數(shù)時(shí),執(zhí)行時(shí)的順序是:最先裝飾的裝飾器,最后一個(gè)執(zhí)行。它遵循了先進(jìn)后出規(guī)則 類似于stack。

五、閉包的作用

閉包的最大特點(diǎn)是可以將父函數(shù)的變量與內(nèi)部函數(shù)綁定,并返回綁定變量后的函數(shù)(也即閉包),此時(shí)即便生成閉包的環(huán)境(父函數(shù))已經(jīng)釋放,閉包仍然存在。

這個(gè)過(guò)程很像類(父函數(shù))生成實(shí)例(閉包),不同的是父函數(shù)只在調(diào)用時(shí)執(zhí)行,執(zhí)行完畢后其環(huán)境就會(huì)釋放,而類則在文件執(zhí)行時(shí)創(chuàng)建,一般程序執(zhí)行完畢后作用域才釋放,因此對(duì)一些需要重用的功能且不足以定義為類的行為,使用閉包會(huì)比使用類占用更少的資源,且更輕巧靈活。

假設(shè)我們僅僅想打印出各類動(dòng)物的叫聲,分別以類和閉包來(lái)實(shí)現(xiàn):

在這里插入圖片描述

樣的,但顯然類的實(shí)現(xiàn)相對(duì)繁瑣,且這里只是想輸出一下動(dòng)物的叫聲,定義一個(gè) Animal 類未免小題大做,而且 voice 函數(shù)在執(zhí)行完畢后,其作用域就已經(jīng)釋放,但 Animal 類及其實(shí)例 dog 的相應(yīng)屬性卻一直貯存在內(nèi)存中。

除此之外,閉包還有很多其他功能,比如用于封裝等,另外,閉包有效的減少了函數(shù)參數(shù)的數(shù)目,這對(duì)并行計(jì)算非常有價(jià)值,比如可以讓每臺(tái)電腦負(fù)責(zé)一個(gè)函數(shù),然后串起來(lái),實(shí)現(xiàn)流水化的作業(yè)等。

六、幾個(gè)小栗子

栗子1:

def outer(f):
    def inner(*arg, **kargs):
        inner.co += 1
        return f(*arg, **kargs)
    inner.co = 0
    return inner

@outer
def cu():
    pass

if __name__ == '__main__':
    cu()
    cu()
    cu()
    print(cu.co)
# 3

栗子2:

下述樣例中,注意點(diǎn):

  • 首先解析裝飾器A,裝飾器裝飾了幾個(gè)類就執(zhí)行幾次,輸出兩次“i賦值”
  • B和C為兩個(gè)對(duì)象,屬性值單獨(dú)累加。
def A(func):
    def inner():
        inner.i += 1
        print("i加1,i={0}".format(inner.i))
    inner.i = 0
    print("i賦值")
    return inner

@A
def B():
    pass

@A
def C():
    pass
B()
B()
B()
C()
C()
print(id(B), id(B.i))
print(id(C), id(C.i))

i賦值
i賦值
i加1,i=1
i加1,i=2
i加1,i=3
i加1,i=1
i加1,i=2
281473235252496 187650677653032
281473235252768 187650677653000

栗子3

  • 裝飾器是在python解釋器加載test函數(shù)的時(shí)候就完成的,即使不調(diào)用test函數(shù),也會(huì)輸出"bbb"和"aaa",輸出順序?yàn)?,dec_b裝飾了test,執(zhí)行輸出bbb,dec_a裝飾了dec_b,執(zhí)行輸出aaa;
  • 執(zhí)行test等同于執(zhí)行dec_a(dec_b(test))
def dec_a(function):    print("aaa")    def inner_func():        print("before function")        function()    return inner_funcdef dec_b(function):    print("bbb")    def inner_func():        function()        print("after function")    return inner_func@dec_a@dec_bdef test():    print("test")test()
bbbaaabefore functiontestafter function

七、特殊的裝飾器

property 裝飾器

參考這篇文章:https://www.tianqiweiqi.com/python-property.html

property 是Python中很贊的概念,它使得面向?qū)ο蟮木幊谈雍?jiǎn)單。

在Python中,property()是一個(gè)內(nèi)置函數(shù),用于創(chuàng)建和返回一個(gè)property對(duì)象。Property對(duì)象有三個(gè)方法,getter(), setter()和delete(),用來(lái)在對(duì)象創(chuàng)建后設(shè)置fget,fset和fdel。

裝飾器(decorator)可以給函數(shù)動(dòng)態(tài)加上功能,對(duì)于類的方法,裝飾器一樣起作用。Python內(nèi)置的@property裝飾器就是負(fù)責(zé)把一個(gè)方法變成屬性調(diào)用的。屬性是對(duì)事物某種特性的抽象,面向?qū)ο缶幊讨幸粋€(gè)重要概念;區(qū)別于字段,它通常表示為字段的擴(kuò)展,加以訪問(wèn)與設(shè)置保護(hù)機(jī)制。

1. 我們?yōu)槭裁葱枰玫絧roperty

博文中假設(shè)了一種場(chǎng)景,假設(shè)我們有一個(gè)存儲(chǔ)并轉(zhuǎn)化溫度的需求,可以通過(guò)類實(shí)現(xiàn):

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

然后通過(guò)類實(shí)例進(jìn)行溫度的設(shè)定和獲取,且可以看到這個(gè)屬性已經(jīng)被添加man.__dict__中了。

>>> man = Celsius()
>>> man.temperature = 37
>>> man.temperature
37
>>> man.to_fahrenheit()
98.60000000000001

>>> man.__dict__
{'temperature': 37}

但是此時(shí)如果我們需要對(duì)溫度的設(shè)定進(jìn)行一定的約束,此前的方案是沒(méi)辦法做到的。

2. 使用Getters和Setters

對(duì)于上邊的約束,一個(gè)很容易想到的解決方案是隱藏其溫度屬性(使其私有化),并且定義新的用于操作溫度屬性的getter和setter接口??梢赃@么實(shí)現(xiàn):

class Celsius:
    def __init__(self, temperature = 0):
        self.set_temperature(temperature)
 
    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    def get_temperature(self):
        return self._temperature
 
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value

上述方案雖然滿足了基本需求,但是有個(gè)問(wèn)題是,在賦值和調(diào)用時(shí),需要修改調(diào)用方式,例如obj.temperature需改為obj.get_temperature(),obj.temperature = val改為obj.set_temperature(val)。

我們希望我們的更新是不向后兼容地。這就是需要property閃亮登場(chǎng)的地方。

3. property的作用

對(duì)于上邊的問(wèn)題,Python式的解決方式是使用property,在setter中進(jìn)行參數(shù)校驗(yàn):

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature
 
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
 
    @property
    def temperature(self):
        print("Getting value")
        return self._temperature
 
    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

在Python中,property()是一個(gè)內(nèi)置函數(shù),用于創(chuàng)建和返回一個(gè)property對(duì)象。該函數(shù)的簽名為:

property(fget=None, fset=None, fdel=None, doc=None)

只定義getter方法,不定義setter方法就是一個(gè)只讀屬性;
否則為可讀可寫屬性,且在setter中進(jìn)行參數(shù)校驗(yàn)。

4. 小栗子

class Student():
    def __init__(self):
        self._score = 10000

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if value < 0:
            print("wrong value")
            return
        self._score = value

    @score.deleter
    def score(self):
        del self._score


a = Student()
a.score = 99
print(a.score)

del a.score
a.score = -1
print(a.score)

# 99
# wrong value
# AttributeError: 'Student' object has no attribute '_score'

staticmethod裝飾器和classmethod裝飾器

python面向?qū)ο缶幊讨?,類中定義的方法:

  • @classmethod 裝飾的類方法:第一個(gè)參數(shù)必須是cls
  • @staticmethod 裝飾的靜態(tài)方法:和普通的函數(shù)沒(méi)有區(qū)別
  • 不帶裝飾器的實(shí)例方法:第一個(gè)參數(shù)必須是 self

以一個(gè)簡(jiǎn)單的代碼為例,執(zhí)行方式如下:

class A(object):   # 創(chuàng)建一個(gè)類對(duì)象,初始化類屬性和方法
    def m1(self, n):
        print("self:", self)

    @classmethod
    def m2(cls, n):
        print("cls:", cls)

    @staticmethod
    def m3(n):
        pass

a = A() # 調(diào)用類構(gòu)造器,構(gòu)造實(shí)例對(duì)象a
a.m1(1) # 內(nèi)部把[實(shí)例對(duì)象a]傳遞給[self]進(jìn)行綁定,self和a指向同一個(gè)實(shí)例對(duì)象。
A.m2(1) # 內(nèi)部把[類對(duì)象A]傳遞給[cls],cls和A都指向類對(duì)象。
A.m3(1)

下面分別使用不同的類方法進(jìn)行代碼的測(cè)試:

step1:定義實(shí)例方法count()。

Spam.numInstances為類調(diào)用,直接返回初始化的99;x.numInstances為實(shí)例化調(diào)用,在實(shí)例化時(shí)調(diào)用了init構(gòu)造方法,調(diào)用了實(shí)例方法count,在99的基礎(chǔ)上加1。

Sub.numInstances, Other.numInstances為類調(diào)用,直接返回初始化的1;y1.numInstances, z1.numInstances為實(shí)例化調(diào)用,由于sub和other子類繼承了父類spam,且在內(nèi)部沒(méi)有定義init方法,因此返回父類的init,調(diào)用count,在初始化的基礎(chǔ)上加1。

class Spam:
    numInstances = 99
    def count(self):
        self.numInstances += 1
    def __init__(self):
        self.count()

class Sub(Spam):
    numInstances = 0

class Other(Spam):
    numInstances = 0

x = Spam()
y1, y2 = Sub(), Sub()
z1, z2, z3 = Other(), Other(), Other()
print(x.numInstances, y1.numInstances, z1.numInstances)
print(Spam.numInstances, Sub.numInstances, Other.numInstances)

100 1 1
99 0 0

step2:定義靜態(tài)方法count()。

每次實(shí)例化都會(huì)調(diào)用init方法,調(diào)用count對(duì)類屬性Spam.numInstances的值進(jìn)行累加,因此實(shí)例化幾次,就會(huì)累加多少次。

class Spam:
    numInstances = 99
    @staticmethod
    def count():
        Spam.numInstances += 1
    def __init__(self):
        self.count()

class Sub(Spam):
    numInstances = 0

class Other(Spam):
    numInstances = 0

x = Spam()
y1, y2 = Sub(), Sub()
z1, z2, z3 = Other(), Other(), Other()
print(x.numInstances, y1.numInstances, z1.numInstances)
print(Spam.numInstances, Sub.numInstances, Other.numInstances)

105 0 0
105 0 0

step3:定義類方法count()。

在實(shí)例化Sub和Other子類時(shí),子類內(nèi)部定義了numInstances,因此會(huì)在cls.numInstances += 1時(shí),分別在Sub和Other各自的numInstances 分別進(jìn)行累加,實(shí)例化多少次,進(jìn)行多少次累加。

class Spam:
    numInstances = 99
    @classmethod
    def count(cls):
        cls.numInstances += 1
    def __init__(self):
        self.count()

class Sub(Spam):
    numInstances = 0

class Other(Spam):
    numInstances = 0

x = Spam()
y1, y2 = Sub(), Sub()
z1, z2, z3 = Other(), Other(), Other()
print(x.numInstances, y1.numInstances, z1.numInstances)
print(Spam.numInstances, Sub.numInstances, Other.numInstances)

100 2 3
100 2 3

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

最新評(píng)論

保康县| 大姚县| 称多县| 隆德县| 会同县| 鄂尔多斯市| 图们市| 肥乡县| 东港市| 永仁县| 江达县| 仁布县| 师宗县| 巩留县| 西安市| 昌宁县| 忻城县| 津市市| 霍邱县| 兴化市| 孟村| 抚宁县| 荃湾区| 乐至县| 临澧县| 静乐县| 祁连县| 泸西县| 蓝田县| 浦江县| 施甸县| 新民市| 潼南县| 新巴尔虎左旗| 庐江县| 绥江县| 宜春市| 都匀市| 吉安市| 项城市| 张家界市|