为什么必须创建一个新的全局变量来引用 "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
通过反复试验,我发现;为了让这段代码工作,我必须创建一个名为 instance
的 self
副本,并将新的 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
使用 getattr
比 exec
更简单(通常更安全)。沿着这些方向尝试:
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
在这里您可以从列表推导中看到 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.
就没有这样的运气了
我有一个 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
通过反复试验,我发现;为了让这段代码工作,我必须创建一个名为 instance
的 self
副本,并将新的 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
使用 getattr
比 exec
更简单(通常更安全)。沿着这些方向尝试:
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
在这里您可以从列表推导中看到 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.