欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

一、裝飾器

1. 裝飾器的簡單介紹

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

在這里插入圖片描述

舉個栗子:

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

# hello

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

2. 裝飾器的解析過程

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

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

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

二、閉包

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

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

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

舉個栗子:

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

三、閉包中nonlocal語句的使用

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

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

例如在下面的栗子,

counter中可以正常打印常量count,但無法改變count;對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)

為了解決上述不可變變量的修改問題:

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

Nonlocal 與 global 的區(qū)別在于:nonlocal 語句會去搜尋本地變量與全局變量之間的變量,其會優(yōu)先尋找層級關(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)簡單演示了裝飾器的功能,事實(shí)上,裝飾器就是一種的閉包的應(yīng)用,只不過其傳遞的(自由變量)是函數(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>

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

五、閉包的作用

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

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

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

在這里插入圖片描述

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

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

六、幾個小栗子

栗子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,裝飾器裝飾了幾個類就執(zhí)行幾次,輸出兩次“i賦值”
  • B和C為兩個對象,屬性值單獨(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ù)的時候就完成的,即使不調(diào)用test函數(shù),也會輸出"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ū)ο蟮木幊谈雍唵巍?/p>

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

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

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

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

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

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

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

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

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

2. 使用Getters和Setters

對于上邊的約束,一個很容易想到的解決方案是隱藏其溫度屬性(使其私有化),并且定義新的用于操作溫度屬性的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

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

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

3. property的作用

對于上邊的問題,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()是一個內(nèi)置函數(shù),用于創(chuàng)建和返回一個property對象。該函數(shù)的簽名為:

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

只定義getter方法,不定義setter方法就是一個只讀屬性;
否則為可讀可寫屬性,且在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 裝飾的類方法:第一個參數(shù)必須是cls
  • @staticmethod 裝飾的靜態(tài)方法:和普通的函數(shù)沒有區(qū)別
  • 不帶裝飾器的實(shí)例方法:第一個參數(shù)必須是 self

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

class A(object):   # 創(chuàng)建一個類對象,初始化類屬性和方法
    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í)例對象a
a.m1(1) # 內(nèi)部把[實(shí)例對象a]傳遞給[self]進(jìn)行綁定,self和a指向同一個實(shí)例對象。
A.m2(1) # 內(nèi)部把[類對象A]傳遞給[cls],cls和A都指向類對象。
A.m3(1)

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

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

Spam.numInstances為類調(diào)用,直接返回初始化的99;x.numInstances為實(shí)例化調(diào)用,在實(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)部沒有定義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í)例化都會調(diào)用init方法,調(diào)用count對類屬性Spam.numInstances的值進(jìn)行累加,因此實(shí)例化幾次,就會累加多少次。

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子類時,子類內(nèi)部定義了numInstances,因此會在cls.numInstances += 1時,分別在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é)

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

相關(guān)文章

  • Python編程中內(nèi)置的NotImplemented類型的用法

    Python編程中內(nèi)置的NotImplemented類型的用法

    這篇文章主要介紹了Python編程中內(nèi)置的NotImplemented類型的用法,NotImplemented 是Python在內(nèi)置命名空間中的六個常數(shù)之一,下文更多詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-03-03
  • 一文詳解Python垃圾回收

    一文詳解Python垃圾回收

    這篇文章主要介紹了一文詳解Python垃圾回收的相關(guān)資料,需要的朋友可以參考下
    2023-09-09
  • Python的歷史與優(yōu)缺點(diǎn)整理

    Python的歷史與優(yōu)缺點(diǎn)整理

    在本篇文章里小編給大家分享的是關(guān)于Python優(yōu)缺點(diǎn)及基礎(chǔ)知識點(diǎn)整理內(nèi)容,有需要的朋友們可以參考下。
    2020-05-05
  • NVIDIA安裝CUDA的實(shí)現(xiàn)(圖文教程)

    NVIDIA安裝CUDA的實(shí)現(xiàn)(圖文教程)

    本文主要介紹了NVIDIA安裝CUDA的實(shí)現(xiàn),包括系統(tǒng)要求、軟件下載、安裝步驟以及常見問題解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Flask框架debug與配置項(xiàng)的開啟與設(shè)置詳解

    Flask框架debug與配置項(xiàng)的開啟與設(shè)置詳解

    這篇文章主要介紹了Flask框架debug與配置項(xiàng)的開啟與設(shè)置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-09-09
  • Python 中 list 的各項(xiàng)操作技巧

    Python 中 list 的各項(xiàng)操作技巧

    最近在學(xué)習(xí) python 語言。大致學(xué)習(xí)了 python 的基礎(chǔ)語法。覺得 python 在數(shù)據(jù)處理中的地位和它的 list 操作密不可分,今天把相關(guān)基礎(chǔ)操作記錄到腳本之家平臺,需要的的朋友參考下
    2017-04-04
  • 使用openCV去除文字中亂入的線條實(shí)例

    使用openCV去除文字中亂入的線條實(shí)例

    這篇文章主要介紹了使用openCV去除文字中亂入的線條實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-06-06
  • pandas merge報(bào)錯的解決方案

    pandas merge報(bào)錯的解決方案

    這篇文章主要介紹了pandas merge報(bào)錯的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Python讀取YAML文件過程詳解

    Python讀取YAML文件過程詳解

    這篇文章主要介紹了Python讀取YAML文件過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 淺談python新手中常見的疑惑及解答

    淺談python新手中常見的疑惑及解答

    下面小編就為大家?guī)硪黄獪\談python新手中常見的疑惑及解答。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06

最新評論