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

資訊專欄INFORMATION COLUMN

Python Closure

n7then / 2024人閱讀

摘要:在計算機科學(xué)中,閉包又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。閉包被廣泛應(yīng)用于函數(shù)式語言中。運用閉包可以避免對全局變量的使用。將棧頂?shù)脑厝〕?,?chuàng)建元組,并將該元組進棧。

在計算機科學(xué)中,閉包 又稱 詞法閉包 或 函數(shù)閉包,是引用了自由變量的函數(shù)。這個被引用的自由變量將和這個函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。閉包被廣泛應(yīng)用于函數(shù)式語言中。

從上面這段話中可以看出閉包的兩個重要條件是引用自由變量函數(shù),與閉包這個名稱結(jié)合起來看,這個函數(shù)就像是一個包,而這個函數(shù)所引用的變量就好比函數(shù)這個包中封閉起來的東西,包中的東西被緊緊封閉在包中,函數(shù)所引用的變量也被與這個函數(shù)所綁定。

首先來看兩個概念 Nonlocal variable 和 Nested function

Nonlocal variable & Nested function

Nonlocal variable是相對于某個函數(shù)來說的,指的是這個函數(shù)所調(diào)用的在本函數(shù)作用域之外的變量,Nested function指的被定義在一個函數(shù)(outer enclosing function)中的函數(shù),這個nested function可以調(diào)用包圍它的作用域中的變量。

看一個例子

def print_msg(msg):
    # outer enclosing function

    def printer():
        # nested function
        print(msg)

    printer()

>>> print_msg("Hello")
Hello

在這個例子中函數(shù)printer就是一個nested function,而變量msg就是一個nonlocal variable。這里需要注意的是,printer雖然可以訪問msg,但是不可以改變它,如果嘗試更改會出現(xiàn)UnboundLocalError: local variable "msg" referenced before assignment。

def print_msg(msg):
    def printer():
        msg += "a"
        print(msg)
    printer()

>>> print_msg("Hello")
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 5, in print_msg
  File "", line 3, in printer
UnboundLocalError: local variable "msg" referenced before assignment
local variable "msg" referenced before assignment

如果必須要更改這個變量的值,在Python3中新引入的nonlocal語句可以解決。

def print_msg(msg):
    def printer():
        nonlocal msg
        msg += "a"
        print(msg)
    printer()

>>> print_msg("Hello")
Helloa

在Python2中使用global也可解決,但是global會直接查找全局變量,而nonlocal則是按優(yōu)先級從本地-->全局進行搜索。

閉包函數(shù)

下面使外層函數(shù)(outer enclosing function)返回一個函數(shù)

def print_msg(msg):
    def printer():
        print(msg)
    return printer

>>> another = print_msg("Hello")
>>> another()
Hello

print_msg("Hello")返回的函數(shù)賦值給another,再調(diào)用another函數(shù)時,發(fā)現(xiàn)已經(jīng)離開了print_msg函數(shù)的作用域,但是"Hello"已經(jīng)被綁定給another,所以仍然能夠正常調(diào)用,這就是Python中的閉包。

刪除print_msg之后,another仍然能夠正常調(diào)用。

>>> del print_msg
>>> print_msg("Hello")
Traceback (most recent call last):
  File "", line 1, in 
NameError: name "print_msg" is not defined
name "print_msg" is not defined

>>> another()
Hello
閉包的應(yīng)用

當(dāng)符合下面幾個條件時就形成了閉包:

有一個Nested function

這個Nested function訪問了父函數(shù)作用域中的變量

父函數(shù)返回了這個Nested function

閉包主要運用在需要講父函數(shù)作用域中的變量綁定到子函數(shù)的場景之中,在釋放掉父函數(shù)之后子函數(shù)也不會受到影響。運用閉包可以避免對全局變量的使用。對于一個只有需要實現(xiàn)少數(shù)方法的類我們也可以用閉包來替代,這樣做可以減少資源的使用。

下面需要用類定義不同動物的叫聲

class Animal:
    def __init__(self, animal):
        self.animal = animal
    def sing(self, voice):
        return "{} sings {}".format(self.animal, voice)

>>> dog = Animal("dog")
>>> cow = Animal("cow")
>>> dog.sing("wong")
"dog sings wong"
>>> cow.sing("mow")
cow sings mow"

用閉包替代

def make_sing(animal):
    def make_voice(voice):
        return "{} sings {}".format(animal, voice)
    return make_voice

>>> dog = make_sing("dog")
>>> dog("wong")
"dog sings wong"
>>> cow = make_sing("cow")
>>> cow("mow")
"cow sings mow"
閉包與裝飾器

閉包通常用來實現(xiàn)一個通用的功能,Python中的裝飾器就是對閉包的一種應(yīng)用,只不過裝飾器中父函數(shù)的參數(shù)是一個函數(shù),下面這個例子通過裝飾器實現(xiàn)了在子函數(shù)執(zhí)行前后輸出提示信息。

def make_wrap(func):
    def wrapper(*args):
        print("before function")
        func(*args)
        print("after function")
    return wrapper

@make_wrap
def print_msg(msg):
    print(msg)

>>> print_msg("Hello")
before function
Hello
after function

裝飾器也可以進行疊加

def make_another(func):
    def wrapper(*args):
        print("another begin")
        func(*args)
        print("another end")
    return wrapper

@make_another
@make_wrap
def print_msg(msg):
    print(msg)

>>> print_msg("Hello")
another begin
before function
Hello
after function
another end
閉包的內(nèi)部實現(xiàn) Code Object

為了了解閉包的內(nèi)部實現(xiàn),需要用compile命令得出相應(yīng)的code object

>>> code_obj = compile("print_msg("Hello")", "", "single")

這里第一個參數(shù)是一個可以被execeval解析的模塊、語句或者表達式;

第二個參數(shù)是用來存放運行時錯誤的文件;

第三個選擇single模式,與前面第一個參數(shù)填寫的表達式相匹配,如果第一個參數(shù)是表達式則需要用eval模式,如果是模塊則應(yīng)該用exec模式。

下面通過discode_obj反編譯成助記符

>>> dis.dis(code_obj)
  1           0 LOAD_NAME                0 (print_msg)
              2 LOAD_CONST               0 ("Hello")
              4 CALL_FUNCTION            1
              6 PRINT_EXPR
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

Python3中通過__code__訪問函數(shù)的code object(Python2中為func_code)

>>> print_msg.__code__
", line 1>
Cell Object

cell object用來存儲被多個作用域所引用的變量。

比如下面函數(shù)中msgprint_msg所引用,也被printer所引用,所以msg會被存在一個cell object

def print_msg(msg):
    def printer():
        print(msg)
    return printer

查看其__closure__屬性可以驗證我們的想法

>>> print_msg("Hello").__closure__
(,)

盡管這兩個引用都被存在同意個cell object,但是他們?nèi)匀恢辉诟髯缘淖饔糜蛳伦饔谩?/p> 閉包分析

首先反編譯print_msg

>>> dis.dis(print_msg)
  2           0 LOAD_CLOSURE             0 (msg)
              2 BUILD_TUPLE              1
              4 LOAD_CONST               1 (", line 2>)
              6 LOAD_CONST               2 ("print_msg..printer")
              8 MAKE_FUNCTION            8
             10 STORE_FAST               1 (printer)

  4          12 LOAD_FAST                1 (printer)
             14 RETURN_VALUE

LOAD_CLOSURE 0 (msg)將變量msg進棧。

BUILD_TUPLE 1 將棧頂?shù)脑厝〕?,?chuàng)建元組,并將該元組push進棧。

LOAD_CONST 1print_msg.__code__.co_consts[1]中取出,為printercode object的地址,將其push進棧。

LOAD_CONST 2print_msg.__code__.co_consts[2]中取出,將其push進棧。

STORE_FAST 1從棧頂取出之前創(chuàng)建的函數(shù)對象的地址信息賦給局部變量printer(局部變量名記錄在__code__.co_varnames中)
__code__.co_varnames的內(nèi)容為("msg","printer")

將變量msg(記錄在__code__.co_cellvars[0])綁定棧頂?shù)暮瘮?shù)對象地址。

LOAD_FAST 1msg的值壓入棧。

RETURN_VALUE返回棧頂。

可以看到在STORE_FAST 1中將變量msg綁定到了printer函數(shù),從而達到了閉包中內(nèi)部函數(shù)訪問外部函數(shù)變量的效果。

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

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

相關(guān)文章

  • Python 自定義函數(shù)的特殊屬性(收藏專用)

    Python 中通過函數(shù)定義所創(chuàng)建的用戶自定義函數(shù)對象均具有一些特殊屬性,需要注意的是這里介紹的是自定義函數(shù)(function類型)的特殊屬性,而非方法(method 類型)的特殊屬性,函數(shù)和方法的特熟屬性以及默認的返回值可能不盡相同。 對于大多數(shù)特殊屬性,可以通過下面這個例子示范一下: class Test(): def func(self, v = dog): 這里演...

    zhou_you 評論0 收藏0
  • PyTips 0x04 - Python 閉包與作用域

    摘要:項目地址閉包在計算機科學(xué)中,閉包英語,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。這個被引用的自由變量將和這個函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。 項目地址:https://git.io/pytips 閉包(Closure) 在計算機科學(xué)中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是...

    leejan97 評論0 收藏0
  • 說說Python中的閉包 - Closure

    摘要:閉包可以用來在一個函數(shù)與一組私有變量之間創(chuàng)建關(guān)聯(lián)關(guān)系。夾帶私貨外部變量返回的是函數(shù),帶私貨的函數(shù)支持將函數(shù)當(dāng)成對象使用的編程語言,一般都支持閉包。所以說當(dāng)你的裝飾器需要自定義參數(shù)時,一般都會形成閉包。 Python中的閉包不是一個一說就能明白的概念,但是隨著你往學(xué)習(xí)的深入,無論如何你都需要去了解這么一個東西。 閉包的概念 我們嘗試從概念上去理解一下閉包。 在一些語言中,在函數(shù)中可以(嵌...

    leon 評論0 收藏0
  • python基礎(chǔ)知識之函數(shù)初階——閉包

    摘要:我們說觸發(fā)了閉包的函數(shù)叫做閉包函數(shù)閉包最大的特點就是它可以被外層函數(shù)返回后賦值給一個變量,并且攜帶了外層函數(shù)內(nèi)定義的變量例子如下變量為函數(shù)開辟的局部命名空間內(nèi)定義的變量函數(shù)內(nèi)引用了變量的內(nèi)層函數(shù)名被當(dāng)作返回值,此時閉包規(guī)則達成。 什么是閉包? 其實我們在使用函數(shù)過程中不經(jīng)意間就會觸發(fā)閉包,因為總會出于某種原因會在函數(shù)內(nèi)引用或修改上一層函數(shù)的變量,這時就會觸發(fā)閉包 那么什么是閉包?其實就...

    TIGERB 評論0 收藏0
  • Python - 閉包Closure

    摘要:恩如期來啦閉包一函數(shù)作為返回值介紹閉包之前,先了解一下函數(shù)作為返回值的情況。例如之前介紹的裝飾器中,就出現(xiàn)了將函數(shù)作為返回值。當(dāng)執(zhí)行時,相當(dāng)于執(zhí)行,且包含。允許使用關(guān)鍵字創(chuàng)造匿名函數(shù)。例如調(diào)用默認可以把匿名函數(shù)作為返回值返回,例如 恩~ 如期來啦閉包~ 一、函數(shù)作為返回值 介紹閉包之前,先了解一下函數(shù)作為返回值的情況。高階函數(shù)除了可以接收函數(shù)作為參數(shù)外,還可以把函數(shù)作為結(jié)果值返回。...

    Jason_Geng 評論0 收藏0

發(fā)表評論

0條評論

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