Python exec如何与当地人合作?

Python exec如何与当地人合作?,python,python-3.x,exec,locals,Python,Python 3.x,Exec,Locals,我以为这会打印3,但它打印1: def f(): a = 1 exec("a = 3") print(a) 这个问题在文章中有所讨论。最终,要获得这种行为,您需要执行以下操作: def foo(): ldict = {} exec("a=3",globals(),ldict) a = ldict['a'] print(a) 如果您检查,您将看到以下注释: 默认局部变量的作用与下面函数locals()的描述相同:不应尝

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

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

这个问题在文章中有所讨论。最终,要获得这种行为,您需要执行以下操作:

def foo():
    ldict = {}
    exec("a=3",globals(),ldict)
    a = ldict['a']
    print(a)
如果您检查,您将看到以下注释:

默认局部变量的作用与下面函数
locals()
的描述相同:不应尝试修改默认局部变量字典。如果需要在函数exec()返回后查看代码对局部变量的影响,请传递显式局部变量字典

这意味着一个参数
exec
不能安全地执行任何绑定局部变量的操作,包括变量赋值、导入、函数定义、类定义等。如果它使用
global
声明,它可以赋值给全局变量,但不能赋值给局部变量

回想一下,Georg Brandl说:

不允许动态修改函数的局部变量 可能没有几个后果:通常,函数局部变量不是 存储在字典中,但数组,其索引在 从已知区域设置编译时。这至少与新技术相冲突 由exec添加的局部变量。旧的exec声明回避了这一点,因为 编译器知道如果出现不带全局/局部参数的exec 在函数中,该名称空间将是“未优化的”,即不使用 局部数组。由于exec()现在是一个普通函数,编译器会这样做 不知道“exec”可能绑定到什么,因此无法处理 特别地

重点是我的

因此,其要点是Python3可以通过默认情况下不允许这种行为来更好地优化局部变量的使用

为了完整性起见,正如上面的评论中所提到的,这确实在Python2.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

如果您在方法中,则可以执行以下操作:

class Thing():
    def __init__(self):
        exec('self.foo = 2')

x = Thing()
print(x.foo)

无法使用
exec
以这种方式更改函数中的局部变量的原因,以及
exec
以这种方式操作的原因,可以总结如下:

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)")
  • exec
    是一个函数,它与调用它的最内部作用域的作用域共享其局部作用域
  • 每当您在函数的作用域中定义新对象时,它都可以在其本地命名空间中访问,即它将修改
    local()
    字典。在
    exec
    中定义新对象时,它的作用大致相当于:

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


  • Python开始从本地名称空间查找名称。这被称为“合法的方式”。Python从本地名称开始,然后检查封闭的作用域,然后是全局的,最后在内置名称空间中查找名称
  • 更全面的例子如下:

    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\u var
    并更改
    l\u var
    后,它只添加
    g\u var
    ,并保持
    l\u var
    不变

    {'l_var': 111, 'g_var': 222}
    
    l_var
    已更改,因为我们正在一次实例化(一次调用exec)中更改并打印局部变量

    在函数的局部变量和exec的局部变量中,
    l_var
    保持不变,并添加了
    g_var

    Inner function: 
    {}
    {'inner_var': 100}
    {'inner_var': 100}
    
    内部函数
    的本地与exec的本地相同

    ['g_var', 'test']
    
    全局仅包含
    g_var
    和函数名(排除特殊方法后)


    哪个Python版本?这是2.6吗?在我的机器上用Python2.5.4打印3我在Python3中得到1,我猜这是他的版本。
    print(a)
    中的括号可能表示Python3.x。我想在那里试试,但我手边没有。是的,是Python3,很抱歉没有注意到这一点。我明白了,这是Python2.X中从exec中破解出来的locals()的问题。这个问题没有像我希望的那样清楚地记录下来。Exec/locals从2.X到3.X的变化应该在某个地方指出,我认为Exec应该有一个方便的参数来绕过这个优化…@MarkRushakoff我在Exec:TypeError的行中得到一个实现错误:“dict”对象是不可调用的难以置信的是Python核心开发人员没有做任何事情来解决这个问题以任何优雅的方式已经近10年了。我可以确认,在2019年8月的Python版本3.7.2中,这种不受欢迎/意外的行为仍然存在。那些家伙添加了他们的垃圾“特性”,破坏了Python 2的巨大灵活性,并且对人们的抱怨毫不在意。上面提到的bug报告以状态“为我工作”结束,并以Jeremy Hyton的评论结束:“Python的行为符合预期,我认为Georg解决了David的所有问题。”我真的不知道如何称呼这样的人。@AnatolyAlekseev:它被记录在案(在“不支持对默认局部变量的修改”的意义上)),并且没有好的修复方法不涉及将
    exec
    恢复到关键字的状态,不需要此功能的代码中的性能回归,或者执行非常笨拙的操作,使对局部变量的写入传递到“真实”局部变量(这在非CPython解释器中可能不实用)。关键是,
    exec
    一直以来都是一个坏主意,在少数情况下,您需要实现所描述的功能,有一些变通方法(如本答案中所述)。
    ['g_var', 'test']
    
    ---------------------
    
    (5, 10)
    5