2010-05-25 43 views
32

我試圖運行一段使用exec的python代碼。全局和本地人在python exec()

my_code = """ 
class A(object): 
    pass 

print 'locals: %s' % locals() 
print 'A: %s' % A 

class B(object): 
    a_ref = A 
""" 

global_env = {} 
local_env = {} 
my_code_AST = compile(my_code, "My Code", "exec") 
exec(my_code_AST, global_env, local_env) 

print local_env 

導致下面的輸出

locals: {'A': <class 'A'>} 
A: <class 'A'> 
Traceback (most recent call last): 
    File "python_test.py", line 16, in <module> 
    exec(my_code_AST, global_env, local_env) 
    File "My Code", line 8, in <module> 
    File "My Code", line 9, in B 
NameError: name 'A' is not defined 

但是,如果我修改代碼,這一點 -

my_code = """ 
class A(object): 
    pass 

print 'locals: %s' % locals() 
print 'A: %s' % A 

class B(A): 
    pass 
""" 

global_env = {} 
local_env = {} 
my_code_AST = compile(my_code, "My Code", "exec") 
exec(my_code_AST, global_env, local_env) 

print local_env 

然後正常工作 - 給下面的輸出 -

locals: {'A': <class 'A'>} 
A: <class 'A'> 
{'A': <class 'A'>, 'B': <class 'B'>} 

顯然A是pres ent和可訪問 - 第一段代碼出了什麼問題?我使用的是2.6.5,歡呼聲,

科林

*更新1 *

如果我入住的是當地人()裏面的類 -

my_code = """ 
class A(object): 
    pass 

print 'locals: %s' % locals() 
print 'A: %s' % A 

class B(object): 
    print locals() 
    a_ref = A 
""" 

global_env = {} 
local_env = {} 
my_code_AST = compile(my_code, "My Code", "exec") 
exec(my_code_AST, global_env, local_env) 

print local_env 

然後就變成明確說當地人()在兩地都不一樣 -

locals: {'A': <class 'A'>} 
A: <class 'A'> 
{'__module__': '__builtin__'} 
Traceback (most recent call last): 
    File "python_test.py", line 16, in <module> 
    exec(my_code_AST, global_env, local_env) 
    File "My Code", line 8, in <module> 
    File "My Code", line 10, in B 
NameError: name 'A' is not defined 

不過,如果我這樣做,是沒有問題的 -

def f(): 
    class A(object): 
    pass 

    class B(object): 
    a_ref = A 

f() 

print 'Finished OK' 

*更新2 *

好了,所以這裏的文檔 - http://docs.python.org/reference/executionmodel.html

「A類的定義是一個可執行可能使用和定義名稱的語句。這些引用遵循名稱解析的正常規則。類定義的名稱空間成爲該類的屬性字典。在類範圍中定義的名稱在方法中不可見。'

在我看來,'A'應該作爲B的定義的可執行語句中的一個自由變量提供,而這發生在我們調用上面的f()時,而不是當我們使用exec )。這可以更容易地顯示具有以下 -

my_code = """ 
class A(object): 
    pass 

print 'locals in body: %s' % locals() 
print 'A: %s' % A 

def f(): 
    print 'A in f: %s' % A 

f() 

class B(object): 
    a_ref = A 
""" 

其輸出

locals in body: {'A': <class 'A'>} 
A: <class 'A'> 
Traceback (most recent call last): 
    File "python_test.py", line 20, in <module> 
    exec(my_code_AST, global_env, local_env) 
    File "My Code", line 11, in <module> 
    File "My Code", line 9, in f 
NameError: global name 'A' is not defined 

所以我想新的問題是 - 爲什麼不是那些當地人暴露在函數和類定義自由變量 - 它看起來像一個非常標準的封閉場景。

+0

不錯。從未注意到。 – zefciu 2010-05-25 12:07:00

+0

它似乎是在這個問題相同的問題:http://stackoverflow.com/questions/2749655/why-are-closures-broken-within-exec – interjay 2010-05-25 12:38:13

+0

感謝指針 - 我不是一個蟒蛇大師,但是當我打印locals()時,似乎A *已經被編譯爲一個局部變量 - 也就是說它確實知道如何處理它。您突出顯示的問題中的答案是 - '編譯時無法知道a是自由變量,因此它將其編譯爲全局引用' 這裏的問題似乎是locals()在B使用exec時的主體,但不使用函數時(請參閱更新問題)?可能很容易成爲我對這個答案的含義的誤解,雖然... – hawkett 2010-05-25 12:55:26

回答

17

嗯,我認爲這是一個執行錯誤或一個無證的設計決定。問題的關鍵在於模塊範圍中的名稱綁定操作應綁定到全局變量。它的實現方式是,當在模塊級別時,globals()IS locals()(在解釋器中嘗試一個),所以當你做任何名字綁定時,它像往常一樣分配給本地人)字典,這也是全局變量,因此創建了一個全局變量。

當您查找變量時,首先檢查當前的當地人,如果找不到名稱,則遞歸檢查包含變量名稱的局部變量,直到找到變量或到達模塊範圍。如果你達到這個目的,你檢查應該是模塊範圍本地人的全局變量。

>>> exec(compile("import sys\nprint sys._getframe().f_code.co_name", "blah", "exec"), {}, {}) 
<module> 
>>> exec("a = 1\nclass A(object):\n\tprint a\n", {}, {}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<string>", line 2, in <module> 
    File "<string>", line 3, in A 
NameError: name 'a' is not defined 
>>> d = {} 
>>> exec("a = 1\nclass A(object):\n\tprint a\n", d,d) 
1 

這種行爲就是繼承工作(該名稱查找使用的代碼對象的範圍當地人(),它確實有一個在裏面)。最後,在CPython實現中,這是一種醜陋的黑客攻擊,即特殊套接字全局查找。這也導致一些荒謬的人工的情況下 - 例如爲:

>>> def f(): 
...  global a 
...  a = 1 
... 
>>> f() 
>>> 'a' in locals() 
True 

請注意,這是基於與解釋搞亂,而Python語言參考閱讀第4.1節(命名和有約束力的)我所有的推論。儘管這不是確定性的(我沒有打開CPython的源代碼),但我確信我對這種行爲是正確的。

+0

好的 - 感謝那些信息 - 這很麻煩:)我想要使用這樣的locals的主要原因是得到代碼字符串中定義的所有東西,沒有python放入全局變量的所有其他東西。如果我在代碼中打印全局變量(),那麼它就是一個很大的東西字典,這很有意義,但是現在我不知道如何將代碼字符串中定義的東西寫入字典中 - 即只有{'A',,'B',}的字典。我不想手動去除所有東西,而且事先不知道代碼字符串中的內容。 – hawkett 2010-05-25 16:30:24

+1

我要回答這個問題 - 我在這裏提交了一個python bug - 歡呼聲。 – hawkett 2010-05-25 16:50:10

+1

我提交的錯誤是重複的。我已經提出了一個爭論,在2.6+以上修正 - http://bugs.python.org/issue991196 – hawkett 2010-05-26 08:52:27

2

如果你的問題是如何讓exec聲明的行爲像文件範圍一樣,我在鏈接的問題和bug中跟蹤了一些提示,並通過爲全局變量和本地變量傳遞單個字典來實現它。顯然,文件範圍是一種特殊情況,其中本地聲明自動放置在全局範圍內。

exec code in dict() 
6

print locals()globals()後,你會發現爲什麼高管拋出 「沒有定義」 異常的原因,你可以試試這個

d = dict(locals(), **globals()) 
exec (code, d, d) 
+0

這是您可以發回和接收全局/本地人的唯一回應。神奇的答案。然後,您可以使用d作爲從子代碼訪問變量的基礎。 d ['status'],如果你的代碼中有status = {}。謝謝@Zoe – Dovy 2017-03-31 14:45:27

1
my_code = """ 
class A(object): 
    pass 

class B(object): 
    a = A 
""" 

my_code_AST = compile(my_code, "My Code", "exec") 
extra_global = "hi" 
global_env = {} 
exec my_code_AST in global_env 
print "global_env.keys() =", global_env.keys() 
print "B.a =", global_env["B"].a 

打印

global_env.keys() = ['__builtins__', 'A', 'B'] 
B.a = <class 'A'> 

Hawkett ,你說,

我想用這樣的locals的主要原因是得到所有在代碼字符串中定義的東西,沒有python放入全局變量中的所有其他東西。

與exec,如果你的全局__builtins__定義沒有做,EXEC增加一個項目,__builtins__到你的全局變量,所以你得到A,B,和__builtins____builtins__本身是一個很大的字典,但它總是與刪除一個元素相同(只要您等到您的代碼在刪除它之前完成使用它即可)!在exec()here下記錄。

built in functions下爲EVAL文檔說

如果全局詞典存在和缺乏「建宏」,當前全局複製到全局表達解析之前。

但實際上它似乎只在複製__builtins__別人都稱

(注:與:要麼設置全局和當地人一樣,或者說exec my_code_AST in global_env沒有一個單獨的local_env。)