Python變數作用域(通俗易懂)

2020-07-16 10:05:26
Python 中變數的存取許可權取決於其賦值的位置,這個位置被稱為變數的作用域。Python 的作用域共有四種,分別是:區域性作用域(Local,簡寫為 L)、作用於閉包函數外的函數中的作用域(Enclosing,簡寫為 E)、全域性作用域(Global,簡寫為 G)和內建作用域(即內建函數所在模組的範圍,Built-in,簡寫為 B)。

變數在作用域中查詢的順序是 L→E→G→B,即當在區域性找不到時會去區域性外的區域性找(例如閉包),再找不到會在全域性範圍內找,最後去內建函數所在模組的範圍中找。

分別在 L、E、G 範圍內定義的變數的例子如下:
global_var = 0         #全域性作用域
def outer():
    enclosing_var = 1    #閉包函數外的函數中
    def inner():
        local_var = 2            #區域性作用域

內建作用域則是通過 builtins 模組實現的,可以使用以下程式碼檢視當前 Python 版本的預定義變數:
import builtins
dir(builtins)
上述程式碼的執行結果如下所示:

>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']


定義在函數內部的變數擁有一個區域性作用域,定義在函數外的變數擁有全域性作用域。區域性變數只能在其宣告語句所在的函數內部存取,全域性變數可以在整個程式範圍內存取。呼叫函數時,所有在函數內宣告的變數名稱都將被加入到作用域中。

當內部作用域想修改外部作用域的變數時,需要使用 global 和 nonlocal 關鍵字宣告外部作用域的變數,例如:
global_num = 1
def func1():
    enclosing_num = 2
    global global_num    #使用global關鍵字宣告
    print(global_num)
    global_num = 123
    print(global_num)
    def func2():
        nonlocal enclosing_num
        print(enclosing_num)       #使用nonlocal關鍵字宣告
        enclosing_num = 456
    func2 ()
    print(enclosing_num)

func1 ()
print(global_num)
上述程式碼的執行結果如下所示:

>>> global_num = 1
>>> def func1():
...          enclosing_num = 2
...          global global_num    #使用global關鍵字宣告
...          print(global_num)
...          global_num = 123
...          print(global_num)
...          def func2():
...              nonlocal enclosing_num
...              print(enclosing_num)       #使用nonlocal關鍵字宣告
...              enclosing_num = 456
...          func2 ()
...          print(enclosing_num)
   
>>> func1 ()
1
123
2
456
>>> print(global_num)
123


只有模組(module),類(class)和函數(def、lambda)才會引入新的作用域,if/elif/else/、try/except、for/while 等語句則不會引入新的作用域,即外部可以存取在這些語句內定義的變數。