【问题标题】:How to convert this Python 2.7 code to Python 3?如何将此 Python 2.7 代码转换为 Python 3?
【发布时间】:2019-08-21 15:54:21
【问题描述】:

以下代码适用于 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 中才有的“功能”?

注意:代码不能绑定外部作用域的名称。

【问题讨论】:

  • 请注意,Python3 中的help(locals) 包含文本“更新此字典是否会影响本地范围内的名称查找,反之亦然是*实现依赖*,并且不包含任何向后兼容性保证。”
  • @AdamSmith 感谢您指出这一点。欢迎提供依赖 CPython 实现细节的答案。

标签: python python-3.x python-2.7 local-variables cpython


【解决方案1】:

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

myvars = {"var": 123}

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

func()

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

与 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):
        ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 2017-05-10
    • 2014-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多