我有一个包含约20个方法的类,在def __init__(self, ...):
中,我不得不调用许多这些方法(〜9),但是我不想一个个地调用每个单独的方法。 / p>
因此,我采取了简单的方法,并创建了两个列表列表推导,使用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
通过反复试验,我发现了这一点;为了使此代码正常工作,我必须创建名为self
的{{1}}的副本,并使新的instance
变量成为全局变量,然后使用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
答案 0 :(得分:4)
使用getattr
比exec
更简单(通常更安全)。尝试以下方法:
def __init__(self):
suffixes = ["ArticleObjects", "SeriesObjects", ...]
for suffix in suffixes:
method = getattr(self, "create" + suffix)
method()
答案 1 :(得分:4)
这里有很多关于如何避免执行exec的好建议(通常是不好的),但是要回答有关为什么的问题,这与列表理解有更多关系。列表推导创建了一个新的作用域,当您在不使用globals或locals参数的情况下调用exec时,它将使用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就没有这种运气了。
答案 2 :(得分:2)
我不会为此使用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")