为什么必须创建一个新的全局变量来引用 "exec" 中的当前 class 实例?

Why must a new global variable be created to reference the current class instance in "exec"?

我有一个 class,其中包含 ~20 个方法,在 def __init__(self, ...): 中,我必须调用其中的许多方法 (~9),但我不想调用每个方法方法一一

所以我采取了简单的方法并创建了两个列表推导式,它们使用 exec 来调用每个方法:

[exec("self.create%s()" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("self.compile%sPage(self)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]

当我 运行 使用 python3 filename.py 这段代码时,我得到了一个错误,即:

NameError: name 'self' is not defined

通过反复试验,我发现;为了让这段代码工作,我必须创建一个名为 instanceself 副本,并将新的 instance 变量设为全局变量,然后使用 ClassName.methodName(instance) 调用该方法而不是 self.methodName():

工作代码为:

global instance; instance = self
[exec("ClassNamecreate%s(instance)" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("ClassName.compile%sPage(instance)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]

这是为什么?为什么 self 变量在 exec 中未定义,尽管它可用于调用 exec 的范围?

更新: 我正在使用 Python 3.6.7

使用 getattrexec 更简单(通常更安全)。沿着这些方向尝试:

def __init__(self):
    suffixes = ["ArticleObjects", "SeriesObjects", ...]
    for suffix in suffixes:
        method = getattr(self, "create" + suffix)
        method()

我不会为此使用 exec。虽然它可能是最短的版本,但它也可能混淆协作者和代码分析工具。我会改用这样的东西:

class Test:
    def __init__(self):
        for f in (self.createA, self.createB, self.createC):
            f()

    def createA(self):
        print("A")

    def createB(self):
        print("B")

    def createC(self):
        print("C")

这里有很多关于如何避免 exec 语句(这通常是不好的)的好建议,但是要回答你关于 为什么 会发生这种情况的问题,还有更多工作要做与列表理解。列表理解创建一个新的范围,当你调用 exec 时没有全局或本地参数,它使用 locals() 函数:

Note: The default locals act as described for function locals() below

Source

在这里您可以从列表推导中看到 locals() 函数的结果:

class Sample:
    def __init__(self):
        k = 4
        print(locals())
        exec("print(locals())")
        [print(locals()) for x in range(1)]
        [exec("print(locals())") for x in range(1)]
Sample()

输出:

{'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
{'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
{'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}
{'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}

因此,locals() 在 exec 内部或外部是相同的。改变它的是列表理解。只有当您在 exec 语句之外时,解释器才能超越列表理解的局部并在外部范围内找到 self 。一旦你调用 exec.

就没有这样的运气了