如何在使用 `exec` 调用时更新局部变量?

问题描述 投票:0回答:3

我以为这会打印 3,但它打印了 1:

# Python3

def f():
    a = 1
    exec("a = 3")
    print(a)

f()
# 1 Expected 3
python python-3.x exec local-variables
3个回答
78
投票

这个问题在 Python3 错误列表 中有所讨论。最终,要获得此行为,您需要执行以下操作:

def foo():
    ldict = {}
    exec("a=3",globals(),ldict)
    a = ldict['a']
    print(a)

如果您在exec

上查看Python3文档,您会看到以下注释:

默认本地人按照下面函数

locals()
的描述进行操作:不应尝试修改默认本地人字典。如果您需要在函数 exec() 返回后查看代码对本地的影响,请传递显式本地字典。

这意味着单参数

exec
不能安全地执行任何会绑定局部变量的操作,包括变量赋值、导入、函数定义、类定义等。如果它使用
global
声明,它可以分配给全局变量,但不是当地人。

回到 错误报告中的特定消息,Georg Brandl 说:

动态修改函数的局部变量不是 可能没有几个后果:通常,函数局部变量不是 存储在字典中,而是一个数组,其索引在 从已知语言环境编译时间。这至少与新的冲突 由 exec 添加的本地人。旧的 exec 语句规避了这一点,因为 编译器知道如果没有全局/本地参数的 exec 发生 在函数中,该命名空间将是“未优化的”,即不使用 当地人数组。由于 exec() 现在是一个普通函数,编译器 不知道“exec”可能绑定到什么,因此无法处理 特别.

重点是我的。

所以它的要点是Python3可以通过not默认允许这种行为来更好地优化局部变量的使用。

为了完整起见,正如上面评论中提到的,这确实在Python 2.X中按预期工作:

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) 
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def f():
...     a = 1
...     exec "a=3"
...     print a
... 
>>> f()
3

5
投票

如果你在一个方法中,你可以这样做:

# python 2 or 3
class Thing():
    def __init__(self):
        exec('self.foo = 2')
    
x = Thing()
print(x.foo)

您可以在这里阅读更多相关信息


4
投票

您不能以这种方式使用

exec
更改函数内的局部变量的原因,以及为什么
exec
以这种方式运行,可以总结如下:

  1. exec
    是一个函数,它与调用它的最内部范围的范围共享其本地范围。
  2. 每当您在函数范围内定义一个新对象时,它都可以在其本地名称空间中访问,即它将修改
    local()
    字典。当您在
    exec
    中定义一个新对象时,它的作用大致相当于以下内容:

from copy import copy
class exec_type:
    def __init__(self, *args, **kwargs):
        # default initializations
        # ...
        self.temp = copy(locals())

    def __setitem__(self, key, value):
        if var not in locals():
            set_local(key, value)
        self.temp[key] = value

temp
是一个临时命名空间,在每次实例化后重置(每次调用
exec
)。


  1. Python 开始从本地命名空间中查找名称。这被称为 LEGB 方式。 Python 从 Local namespce 开始,然后查看 Enclosing scopes,然后是 Global,最后它在 Buit-in namespace 中查找名称。

一个更全面的例子如下:

g_var = 5

def test():
    l_var = 10
    print(locals())
    exec("print(locals())")
    exec("g_var = 222")
    exec("l_var = 111")
    exec("print(locals())")

    exec("l_var = 111; print(locals())")

    exec("print(locals())")
    print(locals())
    def inner():
        exec("print(locals())")
        exec("inner_var = 100")
        exec("print(locals())")
        exec("print([i for i in globals() if '__' not in i])")

    print("Inner function: ")
    inner()
    print("-------" * 3)
    return (g_var, l_var)

print(test())
exec("print(g_var)")

输出:

{'l_var': 10}
{'l_var': 10}

本地人都一样。

{'l_var': 10, 'g_var': 222}

在添加

g_var
并更改
l_var
后,它只会添加
g_var
并保持
l_var
不变。

{'l_var': 111, 'g_var': 222}

l_var
已更改,因为我们正在一次实例化(一次调用 exec)中更改和打印局部变量。

{'l_var': 10, 'g_var': 222}
{'l_var': 10, 'g_var': 222}

在函数的本地和exec的本地

l_var
不变,添加
g_var

Inner function: 
{}
{'inner_var': 100}
{'inner_var': 100}

inner_function
的本地与exec的本地相同。

['g_var', 'test']

global 只包含

g_var
和函数名(排除特殊方法后)。

---------------------

(5, 10)
5
© www.soinside.com 2019 - 2024. All rights reserved.