再次进行python循环导入(也就是这个设计有什么问题)

ts.*_*ts. 46 python dependencies class-design python-import

让我们考虑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)
Run Code Online (Sandbox Code Playgroud)

测试/ user.py:

from test.team import team

class user:
    def setTeam(self, t):
        if issubclass(t, team.__class__):
            self.team = t
Run Code Online (Sandbox Code Playgroud)

测试/ team.py:

from test.user import user

class team:
    def setLeader(self, u):
        if issubclass(u, user.__class__):
            self.leader = u
Run Code Online (Sandbox Code Playgroud)

现在,当然,我已经获得了循环导入和出色的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}
Run Code Online (Sandbox Code Playgroud)

现在一些用法:

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]
Run Code Online (Sandbox Code Playgroud)

所以,除了这个邪恶的循环导入之外,它的工作原理很好(实现细节省略,但没有什么复杂的).

bob*_*nce 81

循环进口本身并不是一件坏事.这是很自然的team代码依靠useruser做的东西team.

这里更糟糕的做法是from module import member.该team模块试图user在导入时获取该类,并且该user模块正在尝试获取team该类.但是这个team类还不存在,因为你仍处于运行team.py时的第一行user.py.

相反,只导入模块.这样可以使命名空间更清晰,以后可以进行猴子修补,并解决导入问题.因为您只是在导入时导入模块,所以您并不关心其中的尚未定义.当你开始使用课程时,它将是.

所以,test/users.py:

import test.teams

class User:
    def setTeam(self, t):
        if isinstance(t, test.teams.Team):
            self.team = t
Run Code Online (Sandbox Code Playgroud)

测试/ teams.py:

import test.users

class Team:
    def setLeader(self, u):
        if isinstance(u, test.users.User):
            self.leader = u
Run Code Online (Sandbox Code Playgroud)

from test import teams然后teams.Team也没关系,如果你想test少写.那仍然是导入模块,而不是模块成员.

此外,如果Team并且User相对简单,请将它们放在同一模块中.您不需要遵循Java每个文件的一个类别的习惯用法.该isinstance测试和set方法也尖叫unpythonic-Java的疣给我; 取决于你正在做什么,你可能最好使用普通的,非类型检查@property.

  • "天生"有难闻的气味.这就像说"显然"或"肯定".如果世界具有循环依赖关系,并且您使用类来模拟世界,那么您的类可能具有循环依赖关系.是的,在实施层面,他们会让事情变得困难,值得讨论解决方案. (20认同)
  • 让我为你重新指出:循环导入本质上是一件坏事,如果你想要占用你的应用程序的一大块,例如,你可以创建一个库._Within_一个解耦的块,使用循环引用可能是完全合理的. (13认同)
  • 循环进口本质上是一件坏事.如果您想要占用应用程序的一大块,例如,从中创建一个库,那么关于该块的依赖关系的重要一点是它们必须全部从应用程序转到库.依赖于您的应用程序的库对任何人都没用.甚至不是你 - 如果没有将它与应用程序捆绑在一起,你将无法在库上运行测试,这会破坏尝试首先将它们分离的目的.由于循环依赖关系是两种方式,因此不可能将代码拆分为分离的块. (9认同)