Python編程思想(20):變量作用域

李寧老師已經在「極客起源」 微信公衆號推出《Python編程思想》電子書,囊括了Python的核心技術,以及Python的主要函數庫的使用方法。讀者可以在「極客起源」 公衆號中輸入 160442 開始學習。

《Python編程思想》總目錄

《Python編程思想》專欄

在程序中定義一個變量時,這個變量是有作用範圍的。變量的作用範圍被稱爲它的作用域。根據定義變量的位置,變量分爲如下兩種:

  • 局部變量。在函數中定義的變量,包括參數,都被稱爲局部變量;
  • 全局變量。在函數外面、全局範圍內定義的變量,被稱爲全局變量;

每個函數在執行時,系統都會爲該函數分配一塊“臨時內存空間”,也可以稱爲函數棧,所有的局部變量都被保存在這塊臨時內存空間內。當函數執行完成後,這塊內存空間就被釋放了,這些局部變量也就失效了。因此離開函數之後就不能再訪問局部變量了。

全局變量意味着它們可以在所有函數內被訪問。不管是在函數的局部範圍內還是在全局範圍內,都可能存在多個變量,每個變量“持有”該變量的值。從這個角度來看,不管是局部範圍還是全局範圍,這些變量和它們的值就像一個“看不見”的字典,其中變量名就是字典的key,變量值就是字典的 value。實際上,,Python提供瞭如下三個工具函數來獲取指定範圍內的“變量字典”。

  • globals:該函數返回全局範圍內所有變量組成的“變量字典”。

  • locals:該函數返回當前局部範圍內所有變量組成的“變量字典”

  • vars(object):獲取在指定對象範圍內所有變量組成的“變量字典”。如果不傳入 object參數,vars()和 locals()的作用完全相同。

globals和 locals看似完全不同,但它們實際上也是有聯繫的,這兩個函數的區別有如下兩點:

  • locals()函數總是獲取當前局部範圍內所有變量組成的“變量字典”,因此,如果在全局範圍內(在函數之外)調用 locals()函數,同樣會獲取全局範圍內所有變量組成的“變量字典;而globals()函數無論在哪裏執行,總是獲取全局範圍內所有變量組成的“變量字典“;

  • 一般來說,使用 locals()和 globals()獲取的“變量字典”只應該被訪問,不應該被修改。但實際上,不管是使用 globals()還是使用 locals()獲取的全局範圍內的“變量字典”,都可以被修改,而這種修改會真正改變全局變量本身,但通過 locals獲取的局部範圍內的“變量字典”,即使對它修改也不會影響局部變量;

下面的代碼演示瞭如何使用 locals()函數和globals()函數訪問局部範圍和全局範圍內的“變量字典”。

示例代碼:locals_globals_test.py

def test ():
    n = 123
    # 直接訪問n局部變量
    print(n) # 輸出20
    # 訪問函數局部範圍的“變量數組”
    print(locals()) # {'n': 123}
    # 通過函數局部範圍的“變量數組”訪問n變量
    print(locals()['n']) # 123
    # 通過locals函數局部範圍的“變量數組”改變n變量的值
    locals()['n'] = 321
    # 再次訪問n變量的值
    print('n:', n) # 依然輸出123
    # 通過globals函數修改x全局變量
    globals()['x'] = 30
x = 5
y = 33
print(globals()) # {..., 'x': 5, 'y': 33}
# 在全局訪問內使用locals函數,訪問的是全局變量的“變量數組”
print(locals()) # {..., 'x': 5, 'y': 33}
# 直接訪問x全局變量
print(x) # 5
# 通過全局變量的“變量數組”訪問x全局變量
print(globals()['x']) # 5
# 通過全局變量的“變量數組”對x全局變量賦值
globals()['x'] = 654
print(x) # 輸出654
# 在全局範圍內使用locals函數對x全局變量賦值
locals()['x'] = 555
print(x) # 輸出555

運行這段代碼,會輸出如下的結果:

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fac501639d0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/System/Volumes/Data/MyStudio/python/python_knowledge/common_resources/books/我寫的書/免費/Python編程思想/05-函數與lambda表達式/locals_globals_test.py', '__cached__': None, 'test': <function test at 0x7fac4000a170>, 'x': 5, 'y': 33}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fac501639d0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/System/Volumes/Data/MyStudio/python/python_knowledge/common_resources/books/我寫的書/免費/Python編程思想/05-函數與lambda表達式/locals_globals_test.py', '__cached__': None, 'test': <function test at 0x7fac4000a170>, 'x': 5, 'y': 33}
5
5
654
555

從上面程序可以清楚地看出 locals函數用於訪問特定範圍內的所有變量組成的“變量字典”,而 globals函數則用於訪問全局範圍內的全局變量組成的“變量字典”

全局變量默認可以在所有函數內被訪問,但如果在函數中定義了與全局變量同名的變量,此時就會發生局部變量遮蔽全局變量的情形。例子代碼如下:

name = 'Mike'
def test1 ():
    # 直接訪問name全局變量
    print(name) # MIke
    name = '鋼鐵俠'
test1()
print(name)

上面程序中直接訪問name變量,這是允許的,此時程序將會輸出Mike。如果在test1函數最後加如下一行代碼:

name='鋼鐵俠'

再次運行該程序,將會看到如下錯誤。

UnboundLocalError: local variable 'name referenced before assignment

該錯誤提示所訪問的name變量還未定義。這是什麼原因呢?這正是由於程序在test1()函數中增加了“name=鋼鐵俠”一行代碼造成的。Python語法規定:在函數內部對不存在的變量賦值時,默認就是重新定義新的局部變量。因此這行代碼相當於重新定義了name局部變量,這樣name全局變量就被遮蔽了,所以這段代碼就會報錯。

爲了避免這個問題,可以通過兩種方式來修改上面程序。

1.訪問被遮蔽的全局變量

如果程序希望print(name)依然能訪問name全局變量,且在可以在該行代碼之後可重新定義name局部變量,此時可通過 globals函數來實現,將上面程序改爲如下形式即可。

name = 'Mike'
def test ():
    # 直接訪問name全局變量
    print(globals()['name'])  # Mike
    name = '鋼鐵俠'
test()
print(name)  # Mike

2.在函數中聲明全局變量

爲了避免在函數中對全局變量賦值(不是重新定義局部變量),可使用 global語句來聲明全局變量。因此,可將程序改爲如下形式。

name = 'Mike'
def test ():
    # 聲明name是全局變量,後面的賦值語句不會重新定義局部變量
    global name
    # 直接訪問name全局變量
    print(name)  # Mike
    name = '鋼鐵俠'
test()
print(name)  # 鋼鐵俠

增加了“ global name”聲明之後,程序會把name變量當成全局變量,這意味着 test()函數後面對name賦值的語句只是對全局變量賦值,而不是重新定義局部變量。

-----------------支持作者請轉發本文,也可以加李寧老師微信:unitymarvel,或掃描下面二維碼加微信--------

歡迎關注  極客起源  微信公衆號,更多精彩視頻和文章等着你哦!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章