四個函數(shù)搞清 Python 作用域
↑↑↑關(guān)注后"星標(biāo)"簡說Python
人人都可以簡單入門Python、爬蟲、數(shù)據(jù)分析 簡說Python推薦 來源: 簡說Python
作者:鞏慶奎
四個函數(shù)搞清 Python 作用域
Python 內(nèi)置函數(shù)中,和作用域相關(guān)的函數(shù)有 locals、global、vars 以及 dir。日常常用這些函數(shù),但時常分不清準(zhǔn)確用法,試來試去。下面來分析這些函數(shù)是什么意思,有什么差別,一勞永逸學(xué)會 Python 作用域的相關(guān)知識點。
本文先說明這些函數(shù)最直接的功能,然后對特殊情況進(jìn)行解釋,最后說明這些函數(shù)有什么用。
最直接的功能
最直接和常用的功能如下:
locals() 返回當(dāng)前局部作用域的符號表字典。 globals() 返回全局作用域的符號表字典。 vars() 相當(dāng)于 locals () dir() 返回當(dāng)前局部作用域的符號表字典對應(yīng)的 keys()。
來看代碼。
>>> import time
>>> pi = 3.1415
>>> globals()
{'__spec__': None, 'pi': 3.1415, 'time': <module 'time' (built-in)>, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__name__': '__main__', '__doc__': None, '__package__': None, '__builtins__': <module 'builtins' (built-in)>}
這里導(dǎo)入了 time 模塊,定義了 pi 全局變量。調(diào)用 globas () 輸出全局變量,可見是一些系統(tǒng)相關(guān)的內(nèi)置屬性,以及定義的 pi 和導(dǎo)入的 time 。
出于篇幅和方便閱讀考慮,之后輸出都不再列出 __ 開頭的屬性。上例輸出,去掉 __ 開頭屬性為:{'pi': 3.1415, 'time': <module 'time' (built-in)>},閱讀更清晰。
繼續(xù)調(diào)用 locals() 函數(shù),輸出當(dāng)前局部作用域的變量表。因為 locals() 位于交互 REPL 界面,所以當(dāng)前局部作用域?qū)嶋H就是全局作用域,所以兩者輸出一樣。
>>> locals() #去掉 __ 開頭的屬性。
{'pi': 3.1415, 'time': <module 'time' (built-in)>}
繼續(xù),因為 vars() 相當(dāng)于 locals(),所以代碼globals() == locals() == vars()成立,結(jié)果為 True 。
最后調(diào)用 dir(),返回當(dāng)前局部作用域的符號表字典對應(yīng)的 keys()。
>>> dir() #去掉 __ 開頭的屬性
['pi', 'time']
因為 dir() 返回的是局部變量字典的 keys,又因為這里局部變量和全局變量一致,加上 vars() 又相當(dāng)于 locals(),所以實際上,可以得到如下等式。
>>> sorted(dir()) == sorted(globals().keys()) == sorted(locals().keys()) == sorted(vars().keys())
True
目前為止的分析中,locals 和 globals 都在全局作用域中,vars 和 dir 都沒有參數(shù),下面分析特殊情況。
特殊情況
先來分析當(dāng) locals 和 globals 位于函數(shù)內(nèi)部時的情況。
locals 和 globals 位于函數(shù)內(nèi)部
看如下代碼
zs = 'zhangsan'
def hello(name):
print(globals())
print(locals())
print(vars())
print(dir())
print('Hello,%s!'%name )
ls = 'lisi'
hello(ls)
在函數(shù)內(nèi)部,global() 返回的是全局作用域變量,所以 zs,ls 和 hello 函數(shù)都有。而 locals() 返回局部變量和自由變量,這里沒有閉包,只有函數(shù)形參 name。vars() 和 dir() 和上例相同,結(jié)果如下:
{'ls': 'lisi', 'zs': 'zhangsan', 'hello': <function hello at 0x00667DB0>}
{'name': 'lisi'}
{'name': 'lisi'}
['name']
Hello,lisi!
另外,確切地說,globals() 返回的是函數(shù)定義時的全局變量空間。所以,如果 zs 變量和 hello 函數(shù)定義在另一個文檔 lib.py 中,在本程序中 from lib import hello 后,再執(zhí)行,返回的是定義時的全局變量:{'zs': 'zhangsan', 'hello': <function hello at 0x00667DB0>}不會包含調(diào)用空間的全局變量 ls。
下面說明當(dāng) vars 和 dir 有參數(shù)時的情況。
vars 和 dir 帶參數(shù)執(zhí)行
vars 和 dir 函數(shù)有參數(shù)時的功能如下:
vars(obj) 返回 obj 的 dict。 dir(obj) 返回 obj 的屬性。
vars(obj) 返回模塊、類、實例以及有 dict 屬性的對象的 dict 屬性。
模塊、類以及實例中的 dict 指代模塊的命名空間或?qū)傩宰值洹?/p>
如下代碼,新建一個類和類的實例,分別對類和實例進(jìn)行 vars 操作,可見:
class Man:
def __init__(self,age,name):
self.age = age
self.name = name
zs = Man(21,'zhangsan')
print(vars(Man))
print(vars(zs))
這其中,類返回的是編譯相關(guān)的對象,諸如弱引用、模塊名、文檔等。對象返回的是屬性,這里是對象屬性 age 和 name。
{'__weakref__': <attribute '__weakref__' of 'Man' objects>, '__dict__': <attribute '__dict__' of 'Man' objects>, '__module__': '__main__', '__doc__': None, '__init__': <function Man.__init__ at 0x00657390>}
{'age': 21, 'name': 'zhangsan'}
dir(obj) 返回對象的有效屬性的名字,這個有效屬性根據(jù)模塊、類和對象等有所不同,大概是從 dict 中提取的,但又不全然這樣。
雖然常在交互模式下 dir() 查詢對象,但這查詢并不完全。
看實例和類的差別。
print(set(dir(zs))-set(dir(Man)))
以上代碼對 zs 實例和 Man 類的 dir() 執(zhí)行結(jié)果求差集,可知對象實例與類實例相比,多了 {'age', 'name'} 兩個屬性,這也是 zs 實例的應(yīng)有之義。
分析了半天的 globals、locals、vars和 dir,它們有什么用呢?
作用
了解、分析對象用法
我們常在 REPL 命令行中用 dir() 來查看類或?qū)ο笾С值姆椒ā碛械膶傩裕@在認(rèn)識新包、編寫程序需要提示時比較有作用。比如 import time 后,忘記 time 的方法了,可以 dir(time) 一下。
globals 和 locals 可以幫助認(rèn)識函數(shù)的作用域,了解當(dāng)前作用域的變量,分析局部、全局、自由變量。
另外,借助于 globals 和 Locals 可以實現(xiàn)動態(tài)編寫代碼。
動態(tài)編寫代碼
如下代碼,用 exec 動態(tài)執(zhí)行 Python 代碼,生成新的變量 s。這里 exec 第一個參數(shù)時源代碼,第二個參數(shù)是源代碼的全局變量環(huán)境,第三個參數(shù)是源代碼的局部變量環(huán)境。
取當(dāng)前全局變量字典(為了用全局變量 math 中的 pi)做第二個參數(shù)。
取當(dāng)前局部變量字典,并稍作修改,加入 r 局部變量,形成 loc 字典做第三個參數(shù)。
import math
loc = locals()
loc['r'] = 10
print('exec',exec('s = math.pi * r * r'),globals(),loc)
print(s)
print(locals()['s'])
可得計算結(jié)果 314.159。用 print(s) 輸出,說明這里的 s 是動態(tài)生成的。又用 locals()['s'] 輸出,說明這里的 s 賦值給了局部作用域字典。
總結(jié)
本文先說明 globals ()、locals()、vars()、dir() 這些函數(shù)最直接的功能,然后對特殊情況進(jìn)行解釋,最后說明這些函數(shù)在理解變量作用域、分析對象、動態(tài)編碼中的作用。
作者:鞏慶奎,大奎,對計算機、電子信息工程感興趣。gongqingkui at 126.com
--END--
老表薦書
圖書介紹:《實戰(zhàn)大數(shù)據(jù)(Hadoop+Spark+Flink)》詳細(xì)介紹了大數(shù)據(jù)工程師在實際工作中應(yīng)該熟練掌握的大數(shù)據(jù)技術(shù)。全書共8章,分別是大數(shù)據(jù)技術(shù)概述、搭建IDEA開發(fā)環(huán)境及Linux虛擬機、基于Hadoop構(gòu)建大數(shù)據(jù)平臺、基于HBase和Kafka構(gòu)建海量數(shù)據(jù)存儲與交換系統(tǒng)、用戶行為離線分析—構(gòu)建日志采集和分析平臺、基于Spark的用戶行為實時分析、基于Flink的用戶行為實時分析、用戶行為數(shù)據(jù)可視化。
掃碼即可加我微信
老表朋友圈經(jīng)常有贈書/紅包福利活動
學(xué)習(xí)更多: 整理了我開始分享學(xué)習(xí)筆記到現(xiàn)在超過250篇優(yōu)質(zhì)文章,涵蓋數(shù)據(jù)分析、爬蟲、機器學(xué)習(xí)等方面,別再說不知道該從哪開始,實戰(zhàn)哪里找了
優(yōu)秀的讀者都知道,“點贊”傳統(tǒng)美德不能丟 
