[英]How does Python know there is a local variable before encountering its declaration?
def f():
print("Before", locals()) # line 2
print(x); # line 3
x = 2 # line 4
print("After", locals()) # line 5
x = 1
f()
我知道 Python 中的 LEGB 范圍規則。
對於上面的代碼,當我注釋掉第 4 行時,一切都按預期正常執行:對於第 3 行,python 沒有在本地范圍內找到變量x
,因此在找到它的全局范圍內搜索它並打印 1。
但是,當我在沒有注釋的情況下執行整個代碼時,它會引發UnboundLocalError: local variable 'x' referenced before assignment
。
我知道我可以使用非本地和全局,但我的問題是:
我嘗試在類似的問題建議中找到答案,但失敗了。 如果我的任何理解有誤,請更正。
在某種程度上,答案是特定於實現的,因為 Python 只指定了預期的行為,而不是如何實現它。
也就是說,讓我們看看通常的實現 CPython 為f
生成的字節碼:
>>> import dis
>>> dis.dis(f)
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Before')
4 LOAD_GLOBAL 1 (locals)
6 CALL_FUNCTION 0
8 CALL_FUNCTION 2
10 POP_TOP
3 12 LOAD_GLOBAL 0 (print)
14 LOAD_FAST 0 (x)
16 CALL_FUNCTION 1
18 POP_TOP
4 20 LOAD_CONST 2 (2)
22 STORE_FAST 0 (x)
5 24 LOAD_GLOBAL 0 (print)
26 LOAD_CONST 3 ('After')
28 LOAD_GLOBAL 1 (locals)
30 CALL_FUNCTION 0
32 CALL_FUNCTION 2
34 POP_TOP
36 LOAD_CONST 0 (None)
38 RETURN_VALUE
有幾種不同的LOAD_*
操作碼用於檢索各種值。 LOAD_GLOBAL
用於全局范圍內的名稱; LOAD_CONST
用於未分配給任何名稱的本地值。 LOAD_FAST
用於局部變量。 局部變量甚至不按名稱存在,而是按數組中的索引存在。 這就是它們“快”的原因; 它們以數組而不是哈希表的形式提供。 ( LOAD_GLOBAL
也使用整數參數,但這只是名稱數組的索引;名稱本身仍然需要在提供全局范圍的任何映射中查找。)
您甚至可以看到與f
關聯的常量和局部值:
>>> f.__code__.co_consts
(None, 'Before', 2, 'After')
>>> f.__code__.co_varnames
('x',)
LOAD_CONST 1
將Before
放入堆棧,因為f.__code__.co_consts[1] == 'Before'
,而LOAD_FAST 0
將x
的值放入堆棧,因為f.__code__.co_varnames[0] == 'x'
。
這里的關鍵是字節碼是在f
執行之前生成的。 Python 不只是在第一次看到它時執行每一行。 執行def
語句包括:
__code__
屬性中的字節碼。 代碼生成的一部分注意到名稱x
,由於在函數體中某處的賦值(即使該函數在邏輯上不可訪問),是一個本地名稱,因此必須使用LOAD_FAST
訪問。
在調用locals
時(實際上是在第一次使用LOAD_FAST 0
之前),尚未對x
進行分配(即STORE_FAST 0
),因此插槽 0中沒有要查找的本地值。
因為你在f()函數調用之前定義了它,
讓我們試試這個:
def f(y):
print("Before", locals()) # line 2
print(y); # line 3
y = 2 # line 4
print("After", locals()) # line 5
f(x)
x = 1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.