亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

由一個(gè)例子到python的名字空間

XiNGRZ / 2505人閱讀

摘要:當(dāng)程序引用某個(gè)變量的名字時(shí),就會(huì)從當(dāng)前名字空間開(kāi)始搜索。對(duì)于可以看出已經(jīng)被導(dǎo)入到自己的名字空間了,而不是在里面。因此并沒(méi)有涉及到修改名字空間。按照原則,搜到有變量并且是個(gè)然后將其加入到自己的后面的就開(kāi)始讀取的元素,并沒(méi)有影響的名字空間。

源自我的博客

前言

python里面最核心的內(nèi)容就是:名字空間(namespace)


例子引入

例1

#!/usr/bin/env python
# encoding: utf-8


def func1():
    x = 1
    print globals()
    print "before func1:", locals()

    def func2():
        a = 1
        print "before fun2:", locals()
        a += x
        print "after fun2:", locals()

    func2()
    print "after func1:", locals()
    print globals()

if __name__ == "__main__":
    func1()

可以正常輸出結(jié)果: 并且需要注意,在func2使用x變量之前的名字空間就已經(jīng)有了"x":1.

before func1: {"x": 1}
before fun2: {"a": 1, "x": 1}
after fun2: {"a": 2, "x": 1}
after func1: {"x": 1, "func2": }

稍微改一點(diǎn):如下

例2:

#!/usr/bin/env python
# encoding: utf-8


def func1():
    x = 1
    print "before func1:", locals()

    def func2():
        print "before fun2:", locals()
        x += x #就是這里使用x其余地方不變
        print "after fun2:", locals()

    func2()
    print "after func1:", locals()

if __name__ == "__main__":
    func1()

輸出就開(kāi)始報(bào)錯(cuò): 而且在before func2也沒(méi)有了x.

before func1: {"x": 1}
before fun2: {}
Traceback (most recent call last):
  File "test.py", line 18, in 
    func1()
  File "test.py", line 14, in func1
    func2()
  File "test.py", line 11, in func2
    x += x
UnboundLocalError: local variable "x" referenced before assignment

這兩個(gè)例子正好涉及到了python里面最核心的內(nèi)容:名字空間,正好總結(jié)一下,然后在解釋這幾個(gè)例子。


名字空間(Namespace)

比如我們定義一個(gè)"變量"

In [14]: a
NameError: name "a" is not defined

所以,這里更準(zhǔn)確的叫法應(yīng)該是名字。 一些語(yǔ)言中比如c,c++,java 變量名是內(nèi)存地址別名, 而Python 的名字就是一個(gè)字符串,它與所指向的目標(biāo)對(duì)象關(guān)聯(lián)構(gòu)成名字空間里面的一個(gè)鍵值對(duì){name: object},因此可以這么說(shuō),python的名字空間就是一個(gè)字典.。

分類

python里面有很多名字空間,每個(gè)地方都有自己的名字空間,互不干擾,不同空間中的兩個(gè)相同名字的變量之間沒(méi)有任何聯(lián)系一般有4種: LEGB四種

locals: 函數(shù)內(nèi)部的名字空間,一般包括函數(shù)的局部變量以及形式參數(shù)

enclosiing function: 在嵌套函數(shù)中外部函數(shù)的名字空間, 對(duì)fun2來(lái)說(shuō), fun1的名字空間就是。

globals: 當(dāng)前的模塊空間,模塊就是一些py文件。也就是說(shuō),globals()類似全局變量。

__builtins__: 內(nèi)置模塊空間, 也就是內(nèi)置變量或者內(nèi)置函數(shù)的名字空間。

當(dāng)程序引用某個(gè)變量的名字時(shí),就會(huì)從當(dāng)前名字空間開(kāi)始搜索。搜索順序規(guī)則便是: LEGB.

locals  -> enclosing function -> globals -> __builtins

一層一層的查找,找到了之后,便停止搜索,如果最后沒(méi)有找到,則拋出在NameError的異常。這里暫時(shí)先不討論賦值操作。
比如例1中的a = x + 1 這行代碼,需要引用x, 則按照LEGB的順序查找,locals()也就是func2的名字空間沒(méi)有,進(jìn)而開(kāi)始E,也就是func1,里面有,找到了,停止搜索,還有后續(xù)工作,就是把x也加到自己的名字空間,這也是為什么fun2的名字空間里面也有x的原因。

訪問(wèn)方式

其實(shí)上面都已經(jīng)說(shuō)了,這里暫時(shí)簡(jiǎn)單列一下

使用locals()訪問(wèn)局部命名空間

使用globals()訪問(wèn)全局命名空間
這里有一點(diǎn)需要注意,就是涉及到了from A import B 和import A的一點(diǎn)區(qū)別。

#!/usr/bin/env python
# encoding: utf-8

import copy
from copy import deepcopy


def func():
    x = 123
    print "func locals:",locals()

s = "hello world"
if __name__ == "__main__":
    func()
    print "globals:", globals()

輸出結(jié)果:

func locals: {"x": 123}
globals: {"__builtins__": ,
 "__file__": "test.py",
 "__package__": None, 
"s": "hello world",
"func": ,
 "deepcopy": , 
"__name__": "__main__",
 "copy": , 
"__doc__": None}

從輸出結(jié)果可以看出globals()包含了定義的函數(shù),變量等。對(duì)于 "deepcopy": 可以看出deepcopy已經(jīng)被導(dǎo)入到自己的名字空間了,而不是在copy里面。 而導(dǎo)入的import copy則還保留著自身的名字空間。因此要訪問(wèn)copy的方法,就需要使用copy.function了。這也就是為什么推薦使用import module的原因,因?yàn)?b>from A import B這樣會(huì)把B引入自身的名字空間,容易發(fā)生覆蓋或者說(shuō)污染。

生存周期

每個(gè)名字空間都有自己的生存周期,如下:

__builtins__: 在python解釋器啟動(dòng)的時(shí)候,便已經(jīng)創(chuàng)建,直到退出

globals: 在模塊定義被讀入時(shí)創(chuàng)建,通常也一直保存到解釋器退出。

locals : 在函數(shù)調(diào)用時(shí)創(chuàng)建, 直到函數(shù)返回,或者拋出異常之后,銷毀。 另外遞歸函數(shù)每一次均有自己的名字空間。

看著沒(méi)有問(wèn)題,但是有很多地方需要考慮。比如名字空間都是在代碼編譯時(shí)期確定的,而不是執(zhí)行期間。這個(gè)也就可以解釋為什么在例1中,before func2:的locals()里面包含了x: 1 這一項(xiàng)。再看下面這個(gè),

def func():
    if False:
        x = 10 #該語(yǔ)句永遠(yuǎn)不執(zhí)行
    print x

肯定會(huì)報(bào)錯(cuò)的,但是錯(cuò)誤不是

NameError: global name "x" is not defined

而是:

UnboundLocalError: local variable "x" referenced before assignment

雖然x = 10永遠(yuǎn)不會(huì)執(zhí)行,但是在執(zhí)行之前的編譯階段,就會(huì)把x作為locals變量,但是后面編譯到print的時(shí)候,發(fā)現(xiàn)沒(méi)有賦值,因此直接拋出異常,locals()里面便不會(huì)有x。這個(gè)就跟例子2中,before func2里面沒(méi)有x是一個(gè)道理。

賦值

為什么要把賦值多帶帶列出來(lái)呢,因?yàn)橘x值操作對(duì)名字空間的影響很大,而且很多地方需要注意。
核心就是: 賦值修改的是命名空間,而不是對(duì)象, 比如:

a = 10

這個(gè)語(yǔ)句就是把a放入到了對(duì)應(yīng)的命名空間, 然后讓它指向一個(gè)值為10的整數(shù)對(duì)象。

a = []
a.append(1)

這個(gè)就是把a放入到名字空間,然后指向一個(gè)列表對(duì)象, 然而后面的a.append(1)這句話只是修改了list的內(nèi)容,并沒(méi)有修改它的內(nèi)存地址。因此
并沒(méi)有涉及到修改名字空間。
賦值操作有個(gè)特點(diǎn)就是: 賦值操作總是在最里層的作用域.也就說(shuō),只要編譯到了有賦值操作,就會(huì)在當(dāng)前名字空間內(nèi)新創(chuàng)建一個(gè)名字,然后開(kāi)始才綁定對(duì)象。即便該名字已存在于賦值語(yǔ)句發(fā)生的上一層作用域中;

總結(jié) 分析例子

現(xiàn)在再看例子2, 就清晰多了, x += x 編譯到這里時(shí),發(fā)現(xiàn)了賦值語(yǔ)句,于是準(zhǔn)備把x新加入最內(nèi)層名字空間也就是func2中,即使上層函數(shù)已經(jīng)存在了; 但是賦值的時(shí)候,又要用到x的值, 然后就會(huì)報(bào)錯(cuò):

UnboundLocalError: local variable "x" referenced before assignment

這樣看起來(lái)好像就是 內(nèi)部函數(shù)只可以讀取外部函數(shù)的變量,而不能做修改,其實(shí)本質(zhì)還是因?yàn)?b>賦值涉及到了新建locals()的名字。
在稍微改一點(diǎn):

#!/usr/bin/env python
# encoding: utf-8

def func1():
    x = [1,2]
    print "before func1:", locals()

    def func2():
        print "before fun2:", locals()
        x[0] += x[0] #就是這里使用x[0]其余地方不變
        print "after fun2:", locals()

    func2()
    print "after func1:", locals()

if __name__ == "__main__":
    func1()

這個(gè)結(jié)果就是:

before func1: {"x": [1, 2]}
before fun2: {"x": [1, 2]}
after fun2: {"x": [2, 2]}
after func1: {"x": [2, 2], "func2": }

咋正確了呢---這不應(yīng)該要報(bào)錯(cuò)嗎? 其實(shí)不然,就跟上面的a.append(1)是一個(gè)道理。
x[0] += x[0] 這個(gè)并不是對(duì)x的賦值操作。按照LEGB原則, 搜到func1有變量x并且是個(gè)list, 然后將其加入到自己的locals(), 后面的x[0] += x[0], 就開(kāi)始讀取x的元素,并沒(méi)有影響func2的名字空間。另外無(wú)論func1func2的名字空間的x 沒(méi)有什么關(guān)系,只不過(guò)都是對(duì)[1, 2]這個(gè)列表對(duì)象的一個(gè)引用。
這個(gè)例子其實(shí)也給了我們一個(gè)啟發(fā),我們知道內(nèi)部函數(shù)無(wú)法直接修改外部函數(shù)的變量值,如例2,如果借助list的話, 就可以了吧!比如把想要修改的變量塞到一個(gè)list里面,然后在內(nèi)部函數(shù)里面做改變!當(dāng)然python3.x里面有了nonlocal關(guān)鍵字,直接聲明一下就可以修改了??吹竭@里,對(duì)作用域理解應(yīng)該有一點(diǎn)點(diǎn)了吧。

延伸 與閉包的不同

我們都知道閉包是把外部函數(shù)的值放到func.func_closure里面,為什么不像上面的例子一樣直接放到函數(shù)的名字空間呢?
這是因?yàn)?b>locals()空間是在函數(shù)調(diào)用的時(shí)候才創(chuàng)建! 而閉包只是返回了一個(gè)函數(shù), 并沒(méi)有調(diào)用,也就沒(méi)有所謂的空間。

locals()與globals()

在最外層的模塊空間里locals()就是globals()

In [2]: locals() is globals()
Out[2]: True

另外我們可以手動(dòng)修改globals()來(lái)創(chuàng)建名字

In [3]: globals()["a"] = "abcde"
In [4]: a
Out[4]: "abcde"

但是locals()在函數(shù)里面的話, 貌似是不起作用的,如下:

In [5]: def func():
   ...:     x = 10
   ...:     print x
   ...:     print locals()
   ...:     locals()["x"] = 20
   ...:     print x
In [6]: func()
10
{"x": 10}
10

這是因?yàn)榻忉屍鲿?huì)將 locals 名字復(fù)制到 一個(gè)叫FAST的 區(qū)域來(lái)優(yōu)化訪問(wèn)速度,而實(shí)際上解釋器訪問(wèn)對(duì)象時(shí),是從FAST區(qū)域里面讀取的,而非locals()。所以直接修改locals()并不能影響x(可以使用exec 動(dòng)態(tài)訪問(wèn),不再細(xì)述)。 不過(guò)賦值操作,會(huì)同時(shí)刷新locals()FAST區(qū)域。


查了不少資料,說(shuō)了這么多,我只想說(shuō),作為python最核心的東西,名字空間還有很多很多地方需要探究,比如

作用域(scope)與名字空間, 這里只是模糊了二者的區(qū)別

面向?qū)ο?,也就是類的名字空間, 又有不一樣的地方。。。

學(xué)一點(diǎn)記錄一點(diǎn)吧。

參考

1
2
3

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

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

相關(guān)文章

  • Python與家國(guó)天下

    摘要:正如儒家經(jīng)典所闡述修身齊家治國(guó)平天下。除此之外,模塊還有如下最基本的屬性在一個(gè)模塊的全局空間里,有些屬性是全局起作用的,稱之為全局變量,而其它在局部起作用的屬性,會(huì)被稱為局部變量。 導(dǎo)讀:Python貓是一只喵星來(lái)客,它愛(ài)地球的一切,特別愛(ài)優(yōu)雅而無(wú)所不能的 Python。我是它的人類朋友豌豆花下貓,被授權(quán)潤(rùn)色與發(fā)表它的文章。如果你是第一次看到這個(gè)系列文章,那我強(qiáng)烈建議,請(qǐng)先看看它寫(xiě)的前...

    姘擱『 評(píng)論0 收藏0
  • Python與家國(guó)天下

    摘要:正如儒家經(jīng)典所闡述修身齊家治國(guó)平天下。除此之外,模塊還有如下最基本的屬性在一個(gè)模塊的全局空間里,有些屬性是全局起作用的,稱之為全局變量,而其它在局部起作用的屬性,會(huì)被稱為局部變量。 導(dǎo)讀:Python貓是一只喵星來(lái)客,它愛(ài)地球的一切,特別愛(ài)優(yōu)雅而無(wú)所不能的 Python。我是它的人類朋友豌豆花下貓,被授權(quán)潤(rùn)色與發(fā)表它的文章。如果你是第一次看到這個(gè)系列文章,那我強(qiáng)烈建議,請(qǐng)先看看它寫(xiě)的前...

    felix0913 評(píng)論0 收藏0
  • python基礎(chǔ)知識(shí)之函數(shù)初階——命名空間

    摘要:在內(nèi)置命名空間不能使用全局和局部的名字??梢孕蜗蟮乩斫獬蓛?nèi)置命名空間具有最高級(jí)別,不需要定義就可以使用,全局命名空間次之,最低級(jí)是局部命名空間。 python中的命名空間分三種: 內(nèi)置的命名空間,在啟動(dòng)解釋器的時(shí)候自動(dòng)加載進(jìn)內(nèi)存的各種名字所在的空間,比如print,input等不需要定義就可以使用的名字 全局命名空間,就是從上到下所有我們定義的變量名和函數(shù)名所在的空間,是在程序從上到下...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<