具有继承性的包中的循环导入依赖项

时间:2011-12-20 22:35:41

标签: python

我的软件包中基本上有以下设置:

thing.py:

from otherthing import *

class Thing(Base):
    def action(self):
        ...do something with Otherthing()...

subthing.py:

from thing import *

class Subthing(Thing):
    pass

otherthing.py:

from subthing import *

class Otherthing(Base):
    def action(self):
        ... do something with Subthing()...

如果我将所有对象放在一个文件中,它就会起作用,但是该文件会变得太大而且难以维护。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:7)

这是一个可怕的Python循环导入参数,但是,恕我直言,你可以有一个很好的设计,仍然需要循环引用。

所以,试试这种方法:

thing.py:

class Thing(Base):
    def action(self):
        ...do something with otherthing.Otherthing()...

import otherthing

subthing.py:

import thing

class Subthing(thing.Thing):
    pass

otherthing.py:

class Otherthing(Base):
    def action(self):
        ... do something with subthing.Subthing()...

import subthing

这里有几件事情要发生。首先,一些背景知识。

由于在Python中导入工作的方式,当导入该模块的其他模块中的未来导入语句被评估时,将认为已导入正在导入(但尚未完全解析)的模块。因此,您最终可能会对仍处于解析过程中的模块上的符号进行引用 - 如果解析没有将其转换为您需要的符号,则无法找到它并将抛出例外。

解决这个问题的一种方法是使用“尾部导入”。此技术的目的是在潜在触发导入其他模块之前定义引用此模块的其他模块可能需要的任何符号。

处理循环引用的另一种方法是从基于from的导入转换为普通import。这有什么用?当您进行from样式导入时,将导入目标模块,然后在该时刻将在模块对象上查找from语句中引用的符号。

使用正常的import语句,参考的查找会延迟,直到模块上的某个实际属性引用为止。这通常可以推送到一个函数或方法中,在完成所有导入之前通常不会执行该函数或方法。

这两种技术不起作用的情况是在类层次结构中有循环引用时。导入必须在子类定义之前,并且当命中class语句时,表示超类的属性必须在那里。您可以做的最好的事情是使用普通的import,通过模块引用超级类,并希望您可以重新安排足够的其余代码以使其正常工作。

如果您仍然处于困境,另一种可以提供帮助的技术是使用访问器功能来调解一个模块与另一个模块之间的访问。例如,如果您在一个模块中有类A并且想要从另一个模块引用它但由于循环引用而无法引用它,您有时可以创建第三个模块,其中包含一个只返回引用的函数上课A。如果你将它概括为一套访问函数,那么这并不会像听起来那么严重。

如果所有其他方法都失败了,您可以将import语句移动到您的函数和方法中 - 但我通常将其作为最后的手段。

---编辑---

只想添加我最近发现的新内容。在“类”语句中,超类实际上是一个Python表达式。所以,你可以这样做:

>>> b=lambda :object
>>> class A(b()):
...     pass
... 
>>> a=A()
>>> a
<__main__.A object at 0x1fbdad0>
>>> a.__class__.__mro__
(<class '__main__.A'>, <type 'object'>)
>>> 

这允许您定义和导入访问器函数以从另一个类定义访问类。

答案 1 :(得分:-5)

停止编写循环导入。这很简单。 thing无法取决于otherthing中的一切

1)搜索与您完全一样的其他问题。

2)阅读这些答案。

3)重写otherthing,以便thing取决于otherthing的一部分,而不是全部otherthing