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



我有一个包含大约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

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

注意:默认的locals与下面的函数locals((所描述的一样

在这里,您可以从列表理解中看到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,就没有这样的运气了。

使用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")

相关内容

  • 没有找到相关文章

最新更新