我最近遇到了乙個關於 python 全域性變數的問題,如下面這個簡單例子裡展示(當然實際**要比這個複雜的多,這裡只是乙個抽象出來當例子)。例子中foo.py
定義了函式f
,而函式f
呼叫了全域性變數a
:
# foo.py
def f():
print(a)
def main():
global a
a = 5
f()if __name__ == '__main__':
main()
執行上面這個檔案將如預料中的輸出5。在另乙個檔案bar.py
中我們引入上面的f
,**如下
# bar.py
from foo import f
def main():
f()main()
執行bar.py
將報nameerror
錯誤。這是因為a
被定義在foo.py
的main
函式中,而當匯入f
函式時,foo.py
的main
函式並未被執行,所以a
也沒喲被定義。
traceback (most recent call last):
file "bar.py", line 10, in main()
file "bar.py", line 7, in main
f()file "foo.py", line 5, in f
print(a)
nameerror: global name 'a' is not defined
為了修復上面當問題第一反應是在bar.py
中定義全域性變數a
,這樣f
就可以找到變數a
了,如下面的**:
# bar.py
from foo import f
def main():
global a
a = 4
f()main()
然而依舊會報錯,黑人問號臉???
traceback (most recent call last):
file "/tmp/example/bar.py", line 13, in main()
file "/tmp/example/bar.py", line 9, in main
f()file "/tmp/example/foo.py", line 5, in f
print(a)
nameerror: global name 'a' is not defined
python 的global
語句的作用只是提示 python 直譯器,被global
修飾的變數是乙個全域性變數,利用上面例子裡函式f
的反編譯**可以清除的看到這一點:
import dis
from foo import f
dis.dis(f)
5 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
從上面可以看出變數a
被認為是全域性變數。python 中的每乙個函式都擁有乙個__globals__
字典變數,該變數實際是函式所屬模組的__dict__
變數的引用。所以在bar.py
中我們想在bar.main
函式中將全域性變數a
賦值為4,實際改變的是bar.py
的__dict__
字典變數 (注:而不是定義f
的foo.py
的__dict__
字典變數)
# bar.py
def main():
global a
a = 4
print(main.__globals__.keys())
print(main.__globals__['a'])
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'foo', 'f', 'dis', 'main', 'a'])
4
上面的**輸出了main.__globals__
(即bar.__dict__
) 中全域性變數a
的值是4,然而這個值對函式f
來說確是不可見的,因為f.__globals__
實際等於foo.__dict__
(簡單而言就是命名空間不同)
from foo import f
print(f.__globals__)
假設我們在foo.py
所有函式的外部預先定義了全域性變數a
,那麼在將函式f
匯入時,a
會隨著f.__globals__
一同被匯入。但這時被匯入的f.__globals__["a"]
( 即foo.__dict__["a"]
) 和bar.main
中賦值的bar.main.__globals__["a"]
( 即bar.__dict__["a"]
) 仍然不是同乙個變數,即賦值無法改變函式f
的輸出,如下面的例子所示。
# foo.py
a = 3
def f():
print(a)
def main():
global a
a = 5
f()if __name__ == '__main__':
main()
# bar.py
from foo import f
def main():
global a
a = 4
f()main()
執行bar.py
輸出3,而不是 4。
就上述例子而言,如果我們想在bar.py
中改變函式f
的輸出,則需要直接更新其__globals__
變數的值。
# bar.py
from foo import f
def main():
f.__globals__['a'] = 4
f()main()
如上所述,函式的__globals__
變數實際是其所屬模組__dict__
變數的引用。所以為了達到上面修改全域性變數的目的,也可以直接更新foo.__dict__
。修改模組foo
的屬性 (attribute) 值即可直接更新foo.__dict__
。
# bar.py
import foo
from foo import f
def main():
foo.a = 4
f()
如果你曾經使用過執行中給**打補丁的庫,一般就是這麼實現的。直接修改被打補丁的模組的__dict__
中特定的物件或函式。、
上面的例子中的函式f
如果接受輸入變數的話,而不是使用全域性變數,**將更容易被測試。同時可讀性也更好,出了問題也更容易 debug。
# foo.py
def f(a):
print(a)
def main():
a = 5
f(a)
if __name__ == '__main__':
main()
# bar.py
from foo import f
def main():
a = 3
f(a)
python 中global的用法
python中定義函式時,若想在函式內部對函式外的變數進行操作,就需要在函式內部宣告其為global。例子1x 1 def func x 2 func print x 輸出 1 此時沒有使用global關鍵字,無法對全域性變數num進行修改 在func函式中並未在x前面加global,所以func函...
python中的global 學習記錄
以往寫python 也沒有用到過這個關鍵字。今天小接觸了一下,把學習到東西分享給大家。看一下下面這一段 num 9 def test2 global num print num num 0 test2 print num 執行結果 函式內,使用了global關鍵字宣告了變數num,注意,這個num因...
Python中global與nonlocal 宣告
如下 a 10 def foo a 100 執行foo 結果 a 還是10 函式中對變數的賦值,變數始終繫結到該函式的區域性命名空間,使用global 語句可以改變這種行為。a 10 def foo global a a 100 a 10 foo a 100 解析名稱時首先檢查區域性作用域,然後由內...