我的包中基本上有以下设置:
thing.py:
from otherthing import *
class Thing(Base):
def action(self):
...do something with Otherthing()...
Run Code Online (Sandbox Code Playgroud)
subthing.py:
from thing import *
class Subthing(Thing):
pass
Run Code Online (Sandbox Code Playgroud)
otherthing.py:
from subthing import *
class Otherthing(Base):
def action(self):
... do something with Subthing()...
Run Code Online (Sandbox Code Playgroud)
如果我将所有对象放在一个文件中,它就可以工作,但是这个文件会变得太大而且维护起来会更难.我该如何解决这个问题?
这是一个可怕的Python循环导入参数,但是,恕我直言,你可以有一个很好的设计,仍然需要循环引用.
所以,尝试这种方法:
thing.py:
class Thing(Base):
def action(self):
...do something with otherthing.Otherthing()...
import otherthing
Run Code Online (Sandbox Code Playgroud)
subthing.py:
import thing
class Subthing(thing.Thing):
pass
Run Code Online (Sandbox Code Playgroud)
otherthing.py:
class Otherthing(Base):
def action(self):
... do something with subthing.Subthing()...
import subthing
Run Code Online (Sandbox Code Playgroud)
这里有几件事情.首先,一些背景.
由于导入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'>)
>>>
Run Code Online (Sandbox Code Playgroud)
这允许您定义和导入访问器函数以从另一个类定义访问类.