首页 > 解决方案 > 如何将此 Python 2.7 代码转换为 Python 3?

问题描述

以下代码适用于 Python 2.7,用于将局部变量动态注入函数范围:

myvars = {"var": 123}

def func():
    exec("")
    locals().update(myvars)
    print(var)

func()
# assert "var" not in globals()

这有点微妙,但是 exec 语句的存在向编译器表明本地命名空间可能被修改。在参考实现中,它将名称“locals”的查找从 LOAD_GLOBAL 操作转换为 LOAD_NAME 操作,从而可以添加到本地命名空间。

在 Python 3 中,exec变成了一个函数而不是一个语句,并且locals()调用返回了命名空间的副本,其中的修改没有任何效果。

如何在 Python 3 中重新创建这个想法,在函数内动态创建局部变量?或者这是仅在 Python 2.x 中才有的“功能”?

注意:代码不得绑定外部范围内的名称。

标签: pythonpython-3.xpython-2.7local-variablescpython

解决方案


我不推荐它,但您可以将函数的主体放在类语句中:

myvars = {"var": 123}

def func():
    class Bleh:
        locals().update(myvars)
        print(var)

func()

与 Python 2 代码一样,locals()在这种情况下修改工作的事实在技术上没有记录,并且可能会发生变化。类作用域必须使用实际的 dict 来解析局部变量,但理论上,以后的 Python 实现可能会复制该 dictlocals()或类似的东西。

与 Python 2 代码一样,这样做会干扰闭包变量解析,包括以下微妙情况:

myvars = {"var": 123}

def func():
    class Bleh:
        locals().update(myvars)
        print([var for x in (1, 2, 3)])

func()

甚至

def func():
    class Bleh:
        var = 123
        print([var for x in (1, 2, 3)])

func()

这两种情况都会导致 NameError,因为列表推导式创建了一个无法访问var.

在 Python 2 中,尝试使用闭包变量exec会导致 SyntaxError。如果您尝试使用 class 语句复制 Python 3 中的功能,则不会得到任何此类检测。

最后,Python在 class 语句完成后尝试构建一个类,这可能会导致一些奇怪的问题,例如__set_name__方法意外运行。您至少可以通过使用元类来解决此问题:

class NoClass(type):
    def __new__(self, *args, **kwargs):
        return None

def func():
    class Bleh(metaclass=NoClass):
        ...

推荐阅读