Consider the python (3.x) scripts:
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)
Test / user.py:
from test.team import team class user: def setTeam(self, t): if issubclass(t, team.__class__): self.team = t
Test / team.py:
from test.user import user class team: def setLeader(self, u): if issubclass(u, user.__class__): self.leader = u
Now, of course, I have circular imports and a great ImportError.
So, not being pythonista, I have three questions. Primarily:
I am. How can I make this work work?
And, knowing that someone will inevitably say: “Circular imports always indicate a design problem,” the second question arises:
II. Why is this design bad?
And finally, the third:
III. What would be a better alternative?
To be precise, type checking, as mentioned above, is just an example; there is also an index layer based on the class, which allows, for example. find all users that are members of one team (the user class has many subclasses, so the index doubles for users in general and for each particular subclass) or all the teams that provided the user as a member
Edit:
Hope a more detailed example clarifies what I'm trying to achieve. Files omitted for reading (but having one source file of 300 KB scares me somehow, so please assume that each class is in a different file)
# 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}
and now some use:
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]
So, it works great (implementation details are omitted, but nothing complicated) besides this wicked circular import thing.