Python exec()函数在2.7以上的版本中损坏?错误:未定义“名称”

2024-09-27 21:32:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我发现了python exec()函数的一个奇怪行为。代码如下:

variables = {
    ('foo', 6),
    ('bar', 42)
}

def func():
    for varData in variables:
        varName, varValue = varData
        localVarToEvaluate = varName + ' = varValue'
        try:
            #exec(localVarToEvaluate, globals(), locals())
            exec(localVarToEvaluate)
        except Exception as err:
            print(str(err))

        if varName not in locals():
            print("Variable names '", varName, "can't be found in local scope!")

    if 'foo' in locals():
        print("'foo' OK:", foo)  # exception here
    else:
        print("'foo' not available!")

    if 'bar' in locals():
        print("'bar' OK:", bar)
    else:
        print("'bar' not available!")

func()

我希望变量foobar在结束时由exec()调用创建并打印,Python2.7就是这样。以上所有内容(在3.3、3.4、3.6和3.7上测试)都会引发异常,即foo未定义:

^{pr2}$

奇怪的是,foo和{}是通过运行locals()globals()或{}(也由if语句确认)看到的,但是代码/解释器却看不到它。 更奇怪的是,调试这个脚本并解析任何变量都是成功的(我在# exception here上设置了一个断点,并使用VS代码在调试窗口中键入foofoo已正确解析为值“6”。在

如果相同的代码(函数func()中的内容)没有包装在函数中,那么这将按预期工作,foo和{}被打印出来。在

你知道这里发生了什么吗?在


更新:我进一步简化了这个问题:

# take 1, create local variable 'foo' with value 6. Not in function.
varName = 'foo'
varValue = 42
localVarToEvaluate = varName + ' = varValue'

try:
    exec(localVarToEvaluate)
except Exception as err:
    print(str(err))

if 'foo' in locals():
    # print(locals()['foo']) # (1)
    # print(foo)  # (2)
    print("'foo' OK:", foo)  # (3)


# take 2, create local variable 'bar' with value 42
def func2():
    varName = 'bar'
    varValue = 42
    localVarToEvaluate = varName + ' = varValue'

    try:
        exec(localVarToEvaluate)
    except Exception as err:
        print(str(err))

    if 'bar' in locals():
        # print(locals()['bar']) # (1)
        # print(bar)  # (2)
        #print("'bar' OK:", bar)  # (3)
        pass # uncomment any line above

func2()

执行此代码时,首先:

'foo' OK: 6

打印,然后引发此异常:

Exception has occurred: NameError
name 'bar' is not defined
  ...

注意,这两个代码是相同的,除了'bar'变量是在函数func2()内创建的。在

我感兴趣的不是解决办法而是解释,为什么会这样,为什么第(1)点有效,而(2)和(3)点不起作用。注意,bar变量可以在locals()中看到,但不能通过直接调用它来访问它-但前提是它是在函数内部创建的!在


Tags: 函数代码iniffooexceptionbarok
2条回答

I've reported a bug on a Python Issue Tracker,官方的回答是:

This is currently by design, which means 3.8 is likely the only viable place it can change. It's also not Windows specific so I removed that component (people may remove themselves from nosy).
...
Currently it's basically a read-only proxy, as locals are optimized within functions which is why you can't see updates via the duct.

总之,以这种方式使用的exec()在函数内部是没有用的。在

很多关于这个问题的不清楚的问题用OP解决了。见答案编辑。它可以归结为(导入)钩住局部变量(变量、def、类),以便在定义中使用它们。在

请看下面的答案和内联评论什么是什么和为什么。在

# take 1, create local variable 'foo' with value 6. Not in function.

# >>> code is executed in local-scope <<<

varName = 'foo'
varValue = 42
localVarToEvaluate = varName + ' = varValue'

try:
    exec(localVarToEvaluate)    # (0) dict item becomes {varName : varValue}
    print (localVarToEvaluate)  # (1)  prints > foo = varValue < dict item
except Exception as err:
    print(str(err))

if 'foo' in locals():
    print(locals()['foo'])      # (2)  prints > 42             <             value
    print(foo)                  # (3)  prints > 42             <             value
    print("'foo' OK:", foo)     # (4)  prints > 'foo' OK: 42   < stringtext, value


# take 2, create local variable 'bar' with value 42

def func2(self):

    # >>> code executed inside function and not local-scope <<<

    varName = 'bar'
    varValue = 42
    localVar2Evaluate = varName + ' = varValue'

    try:
        exec(localVar2Evaluate)    # (5) dict item becomes {varName : varValue}
        print (localVar2Evaluate)  # (6) prints > bar = varValue < dict item
    except Exception as err:
        print(str(err))

    print ('local-scope :', '\n', locals())  # (7) {'bar': 42, 'localVar2Evaluate': 'bar = varValue', 'varValue': 42, 'varName': 'bar'}

    if 'bar' in locals():
        print(locals()['bar'])     # (1)
        print(bar)                 # (2)  <  - python is not looking here in the locals() but inside the def for variable `bar` which is not made unless you give it access (hook or reference) via e.g. self.
        print("'bar' OK:", bar)    # (3)
       # pass # uncomment any line above

x = 'a scotch... lets drink.. mystery solved!'
bar = "the local 'bar' variable is now available inside def func2()..  It is: %s" % x
func2(bar)

如您所见,我(import)创建了一个指向本地变量的钩子,varName'bar'将在定义中使用self。它可以是任何名称t.b.h.请参见self上的pydocs,等等

结果是:

^{pr2}$

如果print('\n\n', locals())在func()下面,您将得到以下打印结果:

  1. 'bar':“本地'bar'变量现在在def中可用 函数2()。。它是:一杯苏格兰威士忌。。。让我们喝。。密谋解决了!”在
  2. “localVarToEvaluate”:“foo=varValue”
  3. “varValue”:42
  4. “福”:42
  5. 'varName':'foo'
  6. “x”:“一杯苏格兰威士忌。。。让我们喝。。密谋解决了在
  7. “func2”:“<;”“function func2 at 0x000002B070027F28”>;“#不带“outside”>;”。在

在第7项中,您可以看到func2链接。在

更新4:

在Python2.7.16和3.5.2之间切换显示locals()dict没有变化,globals()dict有一个变化,如下所示。在

在2.7中:'variables': set([('bar', 42), ('foo', 6)])

在3.5中:'variables': {('bar', 42), ('foo', 6)}

。。。这个set()在我看来,它不再像您在3.5中提到的那样有效。在

我试过改编你的剧本:

import sys

print (sys.version)

variables = {('foo', 6), ('bar', 42)}

def func():
    for varData in variables:
        varName, varValue = varData
        localVarToEvaluate = varName + ' = varValue'
        try:
            exec(localVarToEvaluate)
            print ('t2\n', locals())
        except Exception as err:
            print(str(err))

        if varName not in globals():
            print("Variable names '", varName, "can't be found in global scope!")

    if 'foo' in globals():
        print("'foo' OK:", foo)  # exception here
    else:
        print("'foo' not available!")

    if 'bar' in globals():
        print("'bar' OK:", bar)
    else:
        print("'bar' not available!")

print ('t1\n', globals())

func()

然后。。。可能还是执行官。所以我禁用了运行func(),并且globals()中的差别仍然存在。所以我认为这是globals()函数的区别,而不是{}。在

相关问题 更多 >

    热门问题