使用 reload() 打破导入循环。

10 浏览
0 Comments

使用 reload() 打破导入循环。

可能重复:
\nPython中的循环导入\na.py\n

import b
class Abstract(object):
    pass
class Concrete(Abstract):
    def get_newthing(self):
        return b.NewThing()

\n(注意:对于a.py的任何重大重构将是困难的)\nb.py\n

import a
#reload(a)
class NewThing(a.Abstract):
    pass

\n按照当前写法,运行 \"import b, a\" 是可以的,但是运行 \"import a\" 会出现\n

AttributeError: 'module' object has no attribute 'Abstract'

\n因为Python在a.py中到达 \"import b\" 这一行时,尝试访问尚未创建的 \"a.Abstract\"。\n然而,如果我包含 reload 语句,我可以正常地执行 \"import a\",因为Python会回到a.py模块,在继续执行b.py之前创建 Abstract 类。所以它似乎是有效的(尽管在执行 reload 之前应该添加一个 hasattr 检查)。\n我一直在寻找解决这个导入循环问题的方法,但没有看到过类似的建议。在这种情况下使用 reload() 有什么潜在问题吗?

0
0 Comments

问题的原因是由于模块a.py的设计不好,强制导致了循环导入的情况。循环导入通常是需要避免的。最好的解决方法是将Concrete类(以及它所需的import b语句)拆分到一个单独的模块中,该模块可以在不产生循环依赖的情况下同时导入a.py和b.py。

然而,如果对于你的情况来说,这个重构工作太多了,你可以尝试将a.py中的import b语句从顶部移到Abstract的定义之后。这样做将修复你遇到的错误,因为它确保NewThing始终能够看到Abstract的定义。

具体做法如下:

class Abstract(object):
    pass
import b
class Concrete(Abstract):
    def get_newthing(self):
        return b.NewThing()

这是一种最小的改动,但对于这种情况应该可以解决问题。然而,如果Concrete在定义时需要访问NewThing类,那么这种方法将无法修复。

还有其他导入a.py的模块,这些模块反过来又导入b.py。所以我必须将所有这些模块都移到Abstract下面。这似乎很难维护。

将Abstract移动到一个单独的文件中似乎是一个好主意。然而,我并不是真正的a.py的所有者,所以这似乎是一个相当重大的改动(这就是为什么我更喜欢不修改a.py的解决方法)。有没有什么理由认为reload()不是一个好主意?

顺便说一下,我还建议将NewThing移动到a.py中,但是团队中的其他人希望将其保留在一个单独的模块中。

0
0 Comments

使用reload()来打破import循环的问题出现的原因是reload()函数只能在交互式提示中使用。要解决这个循环引用的问题,可以按照以下方式进行修复:

class Abstract(object):
    pass
class Concrete(Abstract):
    def get_newthing(self):
        import b
        return b.NewThing()

更好的做法是重构代码,避免使用循环引用。import b语句可以在模块级别使用,只需将其放在Abstract的定义之后即可。

为什么不应该使用reload来解决这个问题呢?reload函数仅在交互式解释器中使用。在文档的第三句中写道:“如果你使用外部编辑器编辑了模块源文件,并希望在不离开Python解释器的情况下尝试新版本,这将非常有用。”有其他可以解决这个问题的方法,建议使用它们。

0