亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python學(xué)習(xí)之名字,作用域,名字空間(下)

 更新時(shí)間:2022年05月11日 17:27:44   作者:??編程學(xué)習(xí)網(wǎng)????  
這篇文章主要介紹了Python學(xué)習(xí)之名字,作用域,名字空間,緊接上一篇文章內(nèi)容展開(kāi)全文,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助

前言:

這里再回顧一下函數(shù)的local空間,首先我們往global空間添加一個(gè)鍵值對(duì)相當(dāng)于定義一個(gè)全局變量,那么如果往函數(shù)的local空間里面添加一個(gè)鍵值對(duì),是不是也等價(jià)于創(chuàng)建了一個(gè)局部變量呢?

def f1():
    locals()["name "] = "夏色祭"
    try:
        print(name)
    except Exception as e:
        print(e)
f1()  # name 'name' is not defined

對(duì)于全局變量來(lái)講,變量的創(chuàng)建是通過(guò)向字典添加鍵值對(duì)的方式實(shí)現(xiàn)的。因?yàn)槿肿兞繒?huì)一直在變,需要使用字典來(lái)動(dòng)態(tài)維護(hù)。

但對(duì)于函數(shù)來(lái)講,內(nèi)部的變量是通過(guò)靜態(tài)方式存儲(chǔ)和訪問(wèn)的,因?yàn)榫植孔饔糜蛑写嬖谀男┳兞吭诰幾g的時(shí)候就已經(jīng)確定了,我們通過(guò)PyCodeObject的co_varnames即可獲取內(nèi)部都有哪些變量。

所以,雖然我們說(shuō)查找是按照LGB的方式查找,但是訪問(wèn)函數(shù)內(nèi)部的變量其實(shí)是靜態(tài)訪問(wèn)的,不過(guò)完全可以按照LGB的方式理解。

因此名字空間是Python的靈魂,它規(guī)定了Python變量的作用域,使得Python對(duì)變量的查找變得非常清晰。

LEGB規(guī)則

而從Python2.2開(kāi)始,由于引入了嵌套函數(shù),所以最好的方式應(yīng)該是內(nèi)層函數(shù)找不到某個(gè)變量時(shí)先去外層函數(shù)找,而不是直接就跑到global空間里面找。

那么此時(shí)的規(guī)則就是LEGB:

a = 1
def foo():
    a = 2

    def bar():
        print(a)
    return bar
f = foo()
f()
"""
2
"""

調(diào)用f,實(shí)際上調(diào)用的是函數(shù)bar,最終輸出的結(jié)果是2。如果按照LGB的規(guī)則來(lái)查找的話,由于函數(shù)bar的作用域沒(méi)有a、那么應(yīng)該到全局里面找,打印的結(jié)果是1才對(duì)。

但是我們之前說(shuō)了,作用域僅僅是由文本決定的,函數(shù)bar位于函數(shù)foo之內(nèi),所以函數(shù)bar定義的作用域內(nèi)嵌于函數(shù)foo的作用域之內(nèi)。換句話說(shuō),函數(shù)foo的作用域是函數(shù)bar的作用域的直接外圍作用域。

所以應(yīng)該先從foo的作用域里面找,如果沒(méi)有那么再去全局里面找。而作用域和名字空間是對(duì)應(yīng)的,所以最終打印了2。

另外在執(zhí)行f = foo()的時(shí)候,會(huì)執(zhí)行函數(shù)foo中的def bar():語(yǔ)句,這個(gè)時(shí)候解釋器會(huì)將a=2與函數(shù)bar捆綁在一起,然后返回,這個(gè)捆綁起來(lái)的整體就叫做閉包。

所以:閉包 = 內(nèi)層函數(shù) + 引用的外層作用域

這里顯示的規(guī)則就是LEGB,其中E表示enclosing,代表直接外圍作用域。

global表達(dá)式

有一個(gè)很奇怪的問(wèn)題,最開(kāi)始學(xué)習(xí)Python的時(shí)候,筆者也為此困惑了一段時(shí)間,下面來(lái)看一下。

a = 1
def foo():
    print(a)
foo()
"""
1
"""

首先這段代碼打印1,這顯然是沒(méi)有問(wèn)題的,不過(guò)下面問(wèn)題來(lái)了。

a = 1
def foo():
    print(a)
    a = 2
foo()
"""
UnboundLocalError: local variable 'a' referenced before assignment
"""

僅僅是在print語(yǔ)句后面新建了一個(gè)變量a,結(jié)果就報(bào)錯(cuò)了,提示局部變量a在賦值之前就被引用了,這是怎么一回事,相信肯定有人為此困惑。

而想弄明白這個(gè)錯(cuò)誤的原因,需要深刻理解兩點(diǎn):

  • 一個(gè)賦值語(yǔ)句所定義的變量,在這個(gè)賦值語(yǔ)句所在的整個(gè)作用域內(nèi)都是可見(jiàn)的;
  • 函數(shù)中的變量是靜態(tài)存儲(chǔ)、靜態(tài)訪問(wèn)的, 內(nèi)部有哪些變量在編譯的時(shí)候就已經(jīng)確定;

在編譯的時(shí)候,因?yàn)閍 = 2這條語(yǔ)句,所以知道函數(shù)中存在一個(gè)局部變量a,那么查找的時(shí)候就會(huì)在當(dāng)前作用域中查找。但是還沒(méi)來(lái)得及賦值,就print(a)了,所以報(bào)錯(cuò):局部變量a在賦值之前就被引用了。但如果沒(méi)有a = 2這條語(yǔ)句則不會(huì)報(bào)錯(cuò),因?yàn)橹谰植孔饔糜蛑胁淮嬖赼這個(gè)變量,所以會(huì)找全局變量a,從而打印1

更有趣的東西隱藏在字節(jié)碼當(dāng)中,我們可以通過(guò)反匯編來(lái)查看一下:

import dis
a = 1
def g():
    print(a)
dis.dis(g)
"""
  7           0 LOAD_GLOBAL              0 (print)
              2 LOAD_GLOBAL              1 (a)
              4 CALL_FUNCTION            1
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
"""

def f():
    print(a)
    a = 2
dis.dis(f)
"""
 12           0 LOAD_GLOBAL              0 (print)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 POP_TOP

 13           8 LOAD_CONST               1 (2)
             10 STORE_FAST               0 (a)
             12 LOAD_CONST               0 (None)
             14 RETURN_VALUE
"""

中間的序號(hào)代表字節(jié)碼的偏移量,我們先看第二條,g的字節(jié)碼是LOAD_GLOBAL,意思是在global名字空間中查找;而f的字節(jié)碼是LOAD_FAST,表示在local名字空間中查找。因此結(jié)果說(shuō)明Python采用了靜態(tài)作用域策略,在編譯的時(shí)候就已經(jīng)知道了名字藏身于何處。

而且上面的例子也表明,一旦函數(shù)內(nèi)有了對(duì)某個(gè)名字的賦值操作,這個(gè)名字就會(huì)在作用域內(nèi)可見(jiàn),就會(huì)出現(xiàn)在local名字空間中。換句話說(shuō),會(huì)遮蔽外層作用域中相同的名字。

當(dāng)然Python也為我們精心準(zhǔn)備了global關(guān)鍵字,讓我們?cè)诤瘮?shù)內(nèi)部修改全局變量。比如函數(shù)內(nèi)部出現(xiàn)了global a,就表示我后面的a是全局的,直接到global名字空間里面去找,不要在local空間里面找了。

a = 1
def bar():
    def foo():
        global a
        a = 2
    return foo

bar()()
print(a)  # 2
# 當(dāng)然,也可以通過(guò)globals函數(shù)拿到名字空間
# 然后直接修改里面的鍵值對(duì)

但如果外層函數(shù)里面也出現(xiàn)了變量a,而我們想修改的也是外層函數(shù)的a、不是全局的a,這時(shí)該怎么辦呢?Python同樣為我們準(zhǔn)備了關(guān)鍵字: nonlocal,但是使用nonlocal的時(shí)候,必須是在內(nèi)層函數(shù)里面。

a = 1
def bar():
    a = 2
    def foo():
        nonlocal a
        a = "xxx"
    return foo

bar()()
print(a)  # 1
# 外界依舊是1,但是bar里面的a已經(jīng)被修改了

屬性引用與名字引用

屬性引用實(shí)質(zhì)上也是一種名字引用,其本質(zhì)都是到名字空間中去查找一個(gè)名字所引用的對(duì)象。這個(gè)就比較簡(jiǎn)單了,比如a.xxx,就是到a里面去找屬性xxx,這個(gè)規(guī)則是不受LEGB作用域限制的,就是到a里面查找,有就是有、沒(méi)有就是沒(méi)有。

但是有一點(diǎn)需要注意,我們說(shuō)查找會(huì)按照LEGB規(guī)則,但這必須限制在自身所在的模塊內(nèi),如果是多個(gè)模塊就不行了。舉個(gè)栗子:

# a.py
print(name)
# b.py
name = "夏色祭"
import a

關(guān)于模塊的導(dǎo)入我們后續(xù)會(huì)詳細(xì)說(shuō),總之目前在b.py里面執(zhí)行的import a,你可以簡(jiǎn)單認(rèn)為就是把a(bǔ).py里面的內(nèi)容拿過(guò)來(lái)執(zhí)行一遍即可,所以這里相當(dāng)于print(name)。

但是執(zhí)行b.py的時(shí)候會(huì)提示變量name沒(méi)有被定義,可把a(bǔ)導(dǎo)進(jìn)來(lái)的話,就相當(dāng)于print(name),而我們上面也定義name這個(gè)變量了呀。

顯然,即使我們把a(bǔ)導(dǎo)入了進(jìn)來(lái),但是a.py里面的內(nèi)容依舊是處于一個(gè)模塊里面。而我們也說(shuō)了,名稱引用雖然是LEGB規(guī)則,但是無(wú)論如何都無(wú)法越過(guò)自身所在的模塊。print(name)在a.py里面,而變量name被定義在b.py里面,所以不可能跨過(guò)模塊a的作用域去訪問(wèn)模塊b里面的name,因此在執(zhí)行 import a 的時(shí)候會(huì)拋出 NameError。

所以我們發(fā)現(xiàn),雖然每個(gè)模塊內(nèi)部的作用域規(guī)則有點(diǎn)復(fù)雜,因?yàn)橐裱璍EGB;但模塊與模塊之間的作用域還是劃分的很清晰的,就是相互獨(dú)立。

關(guān)于模塊,我們后續(xù)會(huì)詳細(xì)說(shuō)??傊ㄟ^(guò) . 的方式,本質(zhì)上都是去指定的名字空間中查找對(duì)應(yīng)的屬性。

屬性空間

我們知道,自定義的類里面如果沒(méi)有__slots__,那么這個(gè)類的實(shí)例對(duì)象都會(huì)有一個(gè)屬性字典。

class Girl:
    def __init__(self):
        self.name = "古明地覺(jué)"
        self.age = 16
g = Girl()
print(g.__dict__)  # {'name': '古明地覺(jué)', 'age': 16}

# 對(duì)于查找屬性而言, 也是去屬性字典中查找
print(g.name, g.__dict__["name"])  # 古明地覺(jué) 古明地覺(jué)

# 同理設(shè)置屬性, 也是更改對(duì)應(yīng)的屬性字典
g.__dict__["gender"] = "female"
print(g.gender)  # female

當(dāng)然模塊也有屬性字典,本質(zhì)上和類的實(shí)例對(duì)象是一致的。

import builtins
print(builtins.str)  # <class 'str'>
print(builtins.__dict__["str"])  # <class 'str'>
# 另外,有一個(gè)內(nèi)置的變量 __builtins__,和導(dǎo)入的 builtins 等價(jià)
print(__builtins__ is builtins)  # True

另外這個(gè)__builtins__位于 global名字空間里面,然后獲取global名字空間的globals又是一個(gè)內(nèi)置函數(shù),于是一個(gè)神奇的事情就出現(xiàn)了。

print(globals()["__builtins__"].globals()["__builtins__"].
      globals()["__builtins__"].globals()["__builtins__"].
      globals()["__builtins__"].globals()["__builtins__"]
      )  # <module 'builtins' (built-in)>
print(globals()["__builtins__"].globals()["__builtins__"].
      globals()["__builtins__"].globals()["__builtins__"].
      globals()["__builtins__"].globals()["__builtins__"].list("abc")
      )  # ['a', 'b', 'c']

所以global名字空間和builtin名字空間,都保存了指向彼此的指針,不管套娃多少次,都是可以的。

小結(jié)

在 Python 中,一個(gè)名字(變量)的可見(jiàn)范圍由作用域決定,而作用域由語(yǔ)法靜態(tài)劃分,劃分規(guī)則提煉如下:

  • .py文件(模塊)最外層為全局作用域;
  • 遇到函數(shù)定義,函數(shù)體形成子作用域;
  • 遇到類定義,類定義體形成子作用域;
  • 名字僅在其作用域以內(nèi)可見(jiàn);
  • 全局作用域?qū)ζ渌凶饔糜蚩梢?jiàn);
  • 函數(shù)作用域?qū)ζ渲苯幼幼饔糜蚩梢?jiàn),并且可以傳遞(閉包);

與作用域相對(duì)應(yīng), Python在運(yùn)行時(shí)借助PyDictObject對(duì)象保存作用域中的名字,構(gòu)成動(dòng)態(tài)的名字空間 。

這樣的名字空間總共有 4 個(gè):

  • 局部名字空間(local):不同的函數(shù),局部名字空間不同,可以通過(guò)調(diào)用 locals 獲取;
  • 全局名字空間(global):全局唯一,可以通過(guò)調(diào)用 globals 獲??;
  • 閉包名字空間(enclosing);
  • 內(nèi)置名字空間(builtin):可以通過(guò)調(diào)用 builtins__.__dict 獲取;

查找名字時(shí)會(huì)按照LEGB規(guī)則查找,但是注意:無(wú)法跨越文件本身,也就是按照自身文件的LEGB。如果屬性查找都找到builtin空間了,那么證明這已經(jīng)是最后的倔強(qiáng)。如果builtin空間再找不到,那么就只能報(bào)錯(cuò)了,不可能跑到其它文件中找。

到此這篇關(guān)于Python學(xué)習(xí)之名字,作用域,名字空間(下)的文章就介紹到這了,更多相關(guān)Python名字空間內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python機(jī)器學(xué)習(xí)NLP自然語(yǔ)言處理基本操作之命名實(shí)例提取

    Python機(jī)器學(xué)習(xí)NLP自然語(yǔ)言處理基本操作之命名實(shí)例提取

    自然語(yǔ)言處理(?Natural?Language?Processing,?NLP)是計(jì)算機(jī)科學(xué)領(lǐng)域與人工智能領(lǐng)域中的一個(gè)重要方向。它研究能實(shí)現(xiàn)人與計(jì)算機(jī)之間用自然語(yǔ)言進(jìn)行有效通信的各種理論和方法
    2021-11-11
  • 詳解python3中socket套接字的編碼問(wèn)題解決

    詳解python3中socket套接字的編碼問(wèn)題解決

    本篇文章主要介紹了詳解python3中socket套接字的編碼問(wèn)題解決,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • Python圖像處理庫(kù)PIL的ImageEnhance模塊使用介紹

    Python圖像處理庫(kù)PIL的ImageEnhance模塊使用介紹

    這篇文章主要介紹了Python圖像處理庫(kù)PIL的ImageEnhance模塊使用介紹,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Python Charles抓包配置實(shí)現(xiàn)流程圖解

    Python Charles抓包配置實(shí)現(xiàn)流程圖解

    這篇文章主要介紹了Python Charles抓包實(shí)現(xiàn)流程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 在 Pycharm 安裝使用black的方法詳解

    在 Pycharm 安裝使用black的方法詳解

    這篇文章主要介紹了如何在 Pycharm 安裝使用black的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Python3基礎(chǔ)之list列表實(shí)例解析

    Python3基礎(chǔ)之list列表實(shí)例解析

    這篇文章主要介紹了Python3的list列表用法,這是Python3數(shù)據(jù)類型中非常常見(jiàn)的應(yīng)用,需要的朋友可以參考下
    2014-08-08
  • python中的變量命名規(guī)則詳情

    python中的變量命名規(guī)則詳情

    這篇文章主要介紹了python中的變量命名規(guī)則詳情,變量名可以包括字母、數(shù)字、下劃線,但是數(shù)字不能做為開(kāi)頭,變量用的好或不好,和代碼質(zhì)量有著非常重要的聯(lián)系,合理的使用變量,可以讓你的代碼可讀性更高并且更加簡(jiǎn)潔,下面相關(guān)內(nèi)容吧需要的小伙伴可以參考一下
    2022-03-03
  • Sphinx生成python文檔示例圖文解析

    Sphinx生成python文檔示例圖文解析

    這篇文章主要介為大家紹了Sphinx生成python文檔示例圖文解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • Python中的shape()詳解

    Python中的shape()詳解

    這篇文章主要介紹了Python中的shape()詳解,在debug深度學(xué)習(xí)相關(guān)代碼的時(shí)候,很容易出現(xiàn)shape()這樣形式的東西,用來(lái)告知輸出數(shù)據(jù)的形式,需要的朋友可以參考下
    2023-08-08
  • python 中的列表生成式、生成器表達(dá)式、模塊導(dǎo)入

    python 中的列表生成式、生成器表達(dá)式、模塊導(dǎo)入

    這篇文章主要介紹了python中的列表生成式、生成器表達(dá)式、模塊導(dǎo)入 ,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-06-06

最新評(píng)論