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

2024-09-29 21:56:29 发布

您现在位置:Python中文网/ 问答频道 /正文

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


Tags: 方法instance代码inselfforaboutexec
3条回答

关于如何避免exec语句,这里有很多很好的建议(这通常是不好的),但是要回答您关于为什么会发生这种情况的问题,更多的是关于列表理解。列表理解创建一个新的作用域,当您在没有globals或locals参数的情况下调用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语句之外时,解释器可能会超出列表的局部范围,并在外部范围内找到自我。一旦你打电话给执行官就不会有这样的运气了。你知道吗

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

相关问题 更多 >

    热门问题