python循环再次导入(又称这个设计有什么问题)

让我们考虑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)

测试/ user.py:

from test.team import team

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

测试/ team.py:

from test.user import user

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

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

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

一世。 我怎样才能使这件事情工作?

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

II。 为什么这个设计不好?

最后,第三个:

III。 有什么更好的选择?

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

编辑:

我希望更详细的例子能够澄清我试图达到的目标。 为了可读性省略了文件(但是有一个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]

因此,除了这个不圣洁的循环导入之外,它工作得很好(实现细节省略,但没有什么复杂的)。


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

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

相反,只导入模块。 这导致更清晰的命名空间,使得后来的猴子补丁成为可能,并解决了导入问题。 由于您只是在导入时导入模块,因此您并不在意它内部的类尚未定义。 当你开始使用这个课程的时候,它将会是。

所以,test / users.py:

import test.teams

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

测试/ 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少。 这仍然是导入模块,而不是模块成员。

另外,如果TeamUser相对简单,则将它们放在同一个模块中。 您不需要遵循Java单类每文件惯用语。 isinstance测试和set方法也让unpythonic-Java-wart惊叹于我; 取决于你在做什么,使用简单的,非类型检查的@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不必要的类型检查(另见这里)。 只需使用您获得的对象,因为它是用户/团队,并在中断时引发异常(或者在大多数情况下,不需要额外代码即可引发异常)。 离开这个,你通告进口消失(至少现在)。 只要你获得的对象像一个用户/一个团队那样行事,他们可以是任何东西。 (鸭子打字)
  • 小写的类别(这或多或少都是一种品味问题,但普遍接受的标准(PEP 8)有其不同之处
  • setter不需要的地方:你可以说: my_team.leader=user_buser_b.team=my_team
  • 数据一致性问题:如果(my_team.leader.team!=my_team)会怎么样?
  • 链接地址: http://www.djcxy.com/p/40945.html

    上一篇: python circular imports once again (aka what's wrong with this design)

    下一篇: Get name of current class?