python循环再次导入(又名这个devise有什么问题)

让我们考虑python(3.x)脚本:

main.py:

from test.team import team from test.user import user if __name__ == '__main__': u = user() t = team() u.setTeam(t) t.setLeader(u) 

testing/ user.py:

 from test.team import team class user: def setTeam(self, t): if issubclass(t, team.__class__): self.team = t 

testing/ team.py:

 from test.user import user class team: def setLeader(self, u): if issubclass(u, user.__class__): self.leader = u 

当然,我有循环导入和灿烂的ImportError。

所以,不是pythonista,我有三个问题。 首先:

一世。 我怎样才能使这个工作?

而且,知道有人会不可避免地说“通报import总是表明devise问题”,第二个问题是:

II。 为什么这个devise不好?

最后,第三个:

III。 有什么更好的select?

确切地说,上面的types检查只是一个例子,还有一个基于类的索引层,它允许ie。 find所有用户都是一个团队的成员(用户类有许多子类,所以对于一般用户和每个特定的子类,索引都加倍),或者所有给出用户作为成员的团队

编辑:

我希望更详细的例子将澄清我试图达到的目标。 为了可读性而省略的文件(但有一个300kb的源文件吓到了我,所以请假设每个类都在不同的文件中)

 # ENTITY class Entity: _id = None _defs = {} _data = None def __init__(self, **kwargs): self._id = uuid.uuid4() # for example. or randint(). or x+1. self._data = {}.update(kwargs) def __settattr__(self, name, value): if name in self._defs: if issubclass(value.__class__, self._defs[name]): self._data[name] = value # more stuff goes here, specially indexing dependencies, so we can # do Index(some_class, name_of_property, some.object) to find all # objects of some_class or its children where # given property == some.object else: raise Exception('Some misleading message') else: self.__dict__[name] = value def __gettattr__(self, name): return self._data[name] # USERS class User(Entity): _defs = {'team':Team} class DPLUser(User): _defs = {'team':DPLTeam} class PythonUser(DPLUser) pass class PerlUser(DPLUser) pass class FunctionalUser(User): _defs = {'team':FunctionalTeam} class HaskellUser(FunctionalUser) pass class ErlangUser(FunctionalUser) pass # TEAMS class Team(Entity): _defs = {'leader':User} class DPLTeam(Team): _defs = {'leader':DPLUser} class FunctionalTeam(Team): _defs = {'leader':FunctionalUser} 

现在有一些用法:

 t1 = FunctionalTeam() t2 = DLPTeam() t3 = Team() u1 = HaskellUser() u2 = PythonUser() t1.leader = u1 # ok t2.leader = u2 # ok t1.leader = u2 # not ok, exception t3.leader = u2 # ok # now , index print(Index(FunctionalTeam, 'leader', u2)) # -> [t2] print(Index(Team, 'leader', u2)) # -> [t2,t3] 

所以,除了这个不圣洁的循环导入之外,它还是很好的(实现细节,但没有什么复杂的)。

循环import并不是一件坏事。 team代码依靠useruserteam做某些事情是很自然的。

这里糟糕的做法是from module import memberteam模块试图在导入时获取user类,而user模块正在尝试获取team类。 但是team类还不存在,因为在user.py运行时,您仍然位于team.py的第一行。

相反,只导入模块。 这导致更清晰的命名空间,使得后来的猴子修补成为可能,并且解决了导入问题。 因为您只是在导入时导入模块 ,所以您并不在意它里面的尚未定义。 当你开始使用这个课程的时候,

所以,testing/ users.py:

 import test.teams class User: def setTeam(self, t): if isinstance(t, test.teams.Team): self.team = t 

testing/ teams.py:

 import test.users class Team: def setLeader(self, u): if isinstance(u, test.users.User): self.leader = u 

from test import teams ,然后teams.Team也行,如果你想写testless。 这仍然是导入一个模块,而不是模块成员。

而且,如果TeamUser相对简单,则将它们放在同一个模块中。 您不需要遵循Java一个一类的每个文件的习惯用法。 isinstancetesting和set方法也让unpythonic-Java-wart惊叹于我; 取决于你在做什么,你最好使用简单的,非types检查的@property

一世。 要使其工作,您可以使用延期导入。 一种方法是离开user.py并将team.py更改为:

 class team: def setLeader(self, u): from test.user import user if issubclass(u, user.__class__): self.leader = u 

III。 另外,为什么不把团队和用户类放在同一个文件中呢?

糟糕的做法/臭味是以下几点:

  • Probaly不必要的types检查( 另见这里 )。 只要使用你得到的对象,因为它是一个用户/团队,并在发生中断时引发exception(或者在大多数情况下,不需要额外的代码就会引发exception)。 离开这个,你通报import消失(至less现在)。 只要你得到的对象像一个用户/一个团队,他们可以是任何东西。 ( 鸭子打字 )
  • 小写字母(这或多或less都是一个口味问题,但是普遍接受的标准( PEP 8 )却有所不同
  • setter不需要的地方:你可以说: my_team.leader=user_buser_b.team=my_team
  • 数据一致性问题:如果(my_team.leader.team!=my_team)

这是我还没有看到的东西。 这是一个坏主意/直接使用sys.modulesdevise? 在阅读@bobince解决scheme后,我认为我已经了解了整个import业务,但后来遇到了类似于这个问题的问题 。

这里是另一个解决scheme:

 # main.py from test import team from test import user if __name__ == '__main__': u = user.User() t = team.Team() u.setTeam(t) t.setLeader(u) 

 # test/team.py from test import user class Team: def setLeader(self, u): if isinstance(u, user.User): self.leader = u 

 # test/user.py import sys team = sys.modules['test.team'] class User: def setTeam(self, t): if isinstance(t, team.Team): self.team = t 

和文件test/__init__.py文件是空的。 这个工作的原因是因为test.team被首先导入。 当Python正在导入/读取文件时,它将模块附加到sys.modules 。 当我们导入test/user.py ,模块test.team已经被定义了,因为我们在main.py中导入了它。

我开始喜欢这个想法,模块增长相当大,但有function和类相互依赖。 让我们假设有一个名为util.py的文件,这个文件包含很多相互依赖的类。 也许我们可以将代码拆分成相互依赖的不同文件。 我们如何解决循环导入问题?

那么,在util.py文件中,我们只需从其他“私人”文件中导入所有对象,因为这些文件不是直接访问的,而是通过原始文件访问它们:

 # mymodule/util.py from mymodule.private_util1 import Class1 from mymodule.private_util2 import Class2 from mymodule.private_util3 import Class3 

然后在其他每个文件上:

 # mymodule/private_util1.py import sys util = sys.modules['mymodule.util'] class Class1(object): # code using other classes: util.Class2, util.Class3, etc 

 # mymodule/private_util2.py import sys util = sys.modules['mymodule.util'] class Class2(object): # code using other classes: util.Class1, util.Class3, etc 

只要尝试先导入mymodule.utilsys.modules调用就会工作。

最后,我只会指出,这样做是为了帮助用户提高可读性(较短的文件),因此我不会说循环导入“本质上”是不好的。 一切都可以在同一个文件中完成,但我们正在使用这个,以便我们可以分开代码,而不是滚动通过巨大的文件时,我们自己。