簡體   English   中英

python exec()中的全局變量和局部變量

[英]globals and locals in python exec()

我正在嘗試使用exec運行一段python代碼。

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存在並且可以訪問-第一段代碼出了什么問題? 我正在使用2.6.5,歡呼聲,

科林

*更新1 *

如果我檢查類中的locals()-

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()在兩個地方都不相同-

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”應該作為可執行語句(作為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

因此,我想一個新的問題是-為什么這些局部變量不作為函數和類定義中的自由變量公開-似乎是一個非常標准的關閉方案。

好吧,我認為這可能是實現錯誤,也可能是未經證明的設計決策。 問題的症結在於模塊范圍中的名稱綁定操作應綁定到全局變量。 實現的方法是,在模塊級別,globals()是locals()(在解釋器中嘗試該值),因此,當您進行任何名稱綁定時,它會像往常一樣將其分配給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

此行為是繼承起作用的原因(名稱查找使用了代碼對象的作用域locals(),其中確實包含A)。

最后,這是CPython實現中的一個丑陋的駭客,專門對全局變量進行查找。 它還會導致一些荒謬的人為情況-例如:

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

請注意,這是我在閱讀python語言參考的第4.1節(命名和綁定)時基於與解釋器混淆的所有推斷。 盡管這不是確定的(我還沒有打開CPython的源代碼),但我相當確定我對行為是正確的。

print locals()globals() ,您將找到exec引發“未定義”異常的原因,您可以嘗試執行此操作

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

如果您的問題是如何使exec語句的行為類似於文件作用域,我將在鏈接的問題和錯誤中遵循一些提示,並通過為全局變量和局部變量傳遞單個字典來使其工作。 顯然,文件作用域是一種特殊情況,其中本地聲明會自動放置在全局作用域中。

exec code in dict()
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'>

霍克特,你說,

我想要使​​用類似locals的主要原因是要獲取代碼字符串中定義的所有內容,而不要將python放入全局變量中的所有其他內容。

使用exec時,如果您的全局變量沒有定義__builtins__ ,則exec將一個項目__builtins__添加到您的全局變量中,因此您會獲得A,B和__builtins__ __builtins__本身就是一個大字典,但它總是同一個元素刪除(只要你等到你的代碼被刪除之前使用完!)。 ()下記錄的exec 這里

內置函數下的eval文檔說

如果存在globals字典並且缺少' builtins ',則在解析表達式之前將當前的globals復制到globals中。

但實際上似乎只復制了__builtins__

(還有其他人說的話:要么將全局變量和本地變量設置為相同, exec my_code_AST in global_envexec my_code_AST in global_env而無需單獨的local_env。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM