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
代码依靠user
而user
在team
做某些事情是很自然的。
这里糟糕的做法是from module import member
。 team
模块试图在导入时获取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
也行,如果你想写test
less。 这仍然是导入一个模块,而不是模块成员。
而且,如果Team
和User
相对简单,则将它们放在同一个模块中。 您不需要遵循Java一个一类的每个文件的习惯用法。 isinstance
testing和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_b
和user_b.team=my_team
- 数据一致性问题:如果
(my_team.leader.team!=my_team)
?
这是我还没有看到的东西。 这是一个坏主意/直接使用sys.modules
devise? 在阅读@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.util
, sys.modules
调用就会工作。
最后,我只会指出,这样做是为了帮助用户提高可读性(较短的文件),因此我不会说循环导入“本质上”是不好的。 一切都可以在同一个文件中完成,但我们正在使用这个,以便我们可以分开代码,而不是滚动通过巨大的文件时,我们自己。