安全開發:說一說Python的作用域與名稱空間

0x00 前言

在這個系列的開始,介紹了Python文件操作相關的概念,這一節我會說一說對Python的作用域與名稱空間的理解。

很多同學可能會覺得說的東西比較基礎似乎和安全沒有什麼關係,其實不然。我是這麼理解的,know it,then hack it.真正牛逼的漏洞都是對程序本身有深入的理解後挖出來的,『人肉掃描器』沒啥意思,只有真正弄懂了基礎,再去分析漏洞報告,做到舉一反三,我相信0day也會隨之而來。

同時,我更希望看到的是隨著SDL的落地,安全意識的提高,從源頭上「堵住」漏洞,走出『挖漏洞,補漏洞』的怪圈。

不羅嗦了,開始今天的主題。

0x01 Python的名稱空間

什麼是名稱空間?

簡單來說,名稱空間就是存放名字與值綁定關係的地方。

這裡說的名字與值,不單單指變數名與變數值之間的關係。函數名、類名等均為這裡所指的名字。

名稱空間是內存中一塊隔離的空間。

名稱空間還方便了大型項目的協作開發,Python之禪中也提到,「名稱空間是一個偉大的發明,在日常開發中,我們要儘可能多的使用它。」

名稱空間的分類:

名稱空間共分為3類,

1、全局名稱空間

2、局部名稱空間

3、內置名稱空間

註:變數名、函數名、類名等都是文中所指的名字。

內置名稱空間,就是在Python解釋器啟動時生效,在Python解釋器關閉時失效。像一些常見的函數,比如說print()、len()、max()等均存在於內置名稱空間。

全局名稱空間,定義的是文件級別的名字與值之間的綁定關係。舉個例子,比如說我們在當前文件中定義變數x=1、函數def fuck:pass,等諸如此類,未在函數或類中定義的變數與值之間的綁定關係就是全局名稱空間。這類名字與值之間的綁定關係,直接在Python文件的上下文中定義,未在函數或類的內部定義。

局部名稱空間,定義的是函數內部名字與值之間的綁定關係。

在啟動Python解釋器時,內置名稱空間隨之生效。此時可以直接調用一些內置的函數,而不需要對該類函數進行定義。當Python解釋器關閉時,內置名稱空間隨之失效。

執行Python文件時,Python解釋器會將該Python文件級別的名字與值之間的綁定關係存放起來,存放該綁定關係的空間就是全局名稱空間。當該Python文件執行完畢時,全局名稱空間也隨之失效。

在調用函數時,局部名稱就會臨時生效。函數調用結束,局部名稱空間隨之失效。

需要注意的是,類中定義的名字與值之間的綁定關係也可以理解為局部名稱空間。與函數中的名稱空間不同,類定義完,該名稱空間就會產生。除非類被刪除,否則這塊名稱空間不會被回收。

可見,名稱空間的載入順序如下:

1、先載入內置名稱空間

2、再載入全局名稱空間

3、最後有可能載入局部名稱空間

反之,Python解釋器查找名字的順序為,先局部,再全局,最後內置。

0x02 作用域

作用域就是一個作用範圍,簡單理解,也跟找名字有關。

眾所周知,Python的三塊名稱空間有:內置名稱空間、全局名稱空間、局部名稱空間。

這幾個內存空間的特點如下:

內置名稱空間來說,

生效:在Python解釋器啟動時,內置名稱空間生效。

失效:在Python解釋器關閉時,內置名稱空間失效。

全局名稱空間呢?

生效:執行Python文件時,全局名稱空間生效。

失效:當Python文件執行完畢時,全局名稱空間失效。

局部名稱空間:

生效:當函數被調用時,局部名稱空間臨時生效。

失效:函數調用結束,局部名稱空間失效。

圍繞Python的名稱空間,其作用域一共分為兩塊:全局作用域、局部作用域。

全局作用域由內置名稱空間、全局名稱空間兩部分的名字與值之間的綁定關係組成。

其特點為:全局存活,全局有效,伴隨Python文件執行始終。

局部作用域由局部名稱空間的名字與值之間的綁定關係構成。

其特點為:臨時存活,局部有效。

總結一下,名字的查找順尋為LEGB,即locals -> enclosing function -> globals -> builtins。

locals:代表函數內部的名字空間,包括函數內部的局部變數,和形式參數。

enclosing:外部函數嵌套的名字空間,這種形式常見於閉包中。

globals:全局變數,即文件級別定義的名字與值之間的綁定關係。函數定義所在模塊的名稱空間。

builtins:內置模塊的名字空間。

查看作用域的兩個內置函數:globals()、locals()。

globals(),用來查看全局名稱空間的名字。

dir(globals()[__builtins__]),用來查看內置名稱空間的名字。

在內置名稱空間中,存在諸如print、max、len等名字,這就是為什麼我們可以直接調用這些函數,而不需要進行定義的原因。

locals(),用來查看局部名稱空間的名字。

在文件級別而不是在函數中調用locals()查看名字時,與globals()的返回值相同。

作用域關係,在函數定義時就已經確定,與調用位置無關。

x = 23333def f1(): def f2(): print(x) return f2f = f1()print(f)f()def func(): x = 123 f()func()

如上代碼,由於作用域關係在函數定義時就已經確定了,即f()函數的作用域關係在定義時確定x = 23333(全局名稱空間),與調用位置x=123(局部名稱空間)無關,所以最後的輸出結果仍為23333而不是123。

0x03 關鍵字global和nonlocal的引入

x = 1def foo(): x = 10foo()print(x)

該代碼的執行結果為:1

x = 1def foo(): global x x = 10foo()print(x)

該代碼的執行結果為:10

在函數內部(局部名稱空間),要想修改全局名稱空間內變數的值需要使用關鍵字global聲明。

x = 1def foo(): x = 2 def foo2(): nonlocal x x = 13213321 foo2() print(foo:+str(x))foo()print(x)

該代碼的執行結果為:

foo:132133211

在嵌套定義的函數中,要想修改上一級函數中(上一級局部名稱空間)變數的值,需要使用關鍵字nonlocal進行聲明。若上一級不存在該變數,則繼續向上一級尋找,直到找到為止(僅限在函數內部)。找不到則報錯。

0x04 參考鏈接

9. Classes - Python 3.6.4 documentation

推薦閱讀:

網路中的「豆腐渣工程」與真正的二次認證
業務連續性計劃概述(Day008)
安全態勢感知:一些哲學思考
盤點物聯網網路和設備安全的五個誤解
博彩借錢風暴(朋友QQ被盜):我是如何反擊詐騙犯!

TAG:Python | 滲透測試 | 網路安全 |