I am writing in python 3.5, I try to use typing, but I ran into the problem of cyclic import - it became difficult to break the program into modules.

One of the problems is one_to one:

# модуль 1 from mod1 import B class A: def __init__(self): self.__B = B(self) # модуль 2 from mod2 import A class B: def __init__(self, arg: A): self.__a = arg 

Well, and so on. The problem is seriously increasing with the increase in the number of modules.

I want to know if I approach the task incorrectly, or is there a solution to the problem?

PS: I am in the sense that the ImportError exception is raised, and I cannot get rid of it.

    3 answers 3

    PEP 484 - Type Hints recommends using an import module instead of from module import Type and setting the types as strings to reduce the effects of circular dependencies caused by using type hints:

     # mod1.py import mod2 class B: def __init__(self, arg: 'mod2.A') -> None: self.__a = arg 
     # mod2.py import mod1 class A: def __init__(self): self.__b = mod1.B(self) 

    Alternatively, if the classes are so closely related, you can put them in one module or perform another suitable case of refactoring (perhaps a more general type, defined, for example, in mod2.types , should be used in B ). See Type hinting would generate a lot of import statements (possibly leading to circular imports). Use strings instead of imported names?

    • In the form of lines it makes no sense to specify the types. In this case, the tooltips will not work, in fact it is equivalent to the usual commenting. - Mr Fix
    • import module or from module import name , more often, the role will not play either, since the names have not yet been defined. - Mr. Fix
    • In one module, this is of course a solution to the problem with imports, but not a solution to the problem, since it is still important to break the code into modules. - Mr Fix
    • @Mrfix: 1- type checking works with strings, for example, B(object()) results: Argument 1 to "B" has incompatible type "object"; expected "A" Argument 1 to "B" has incompatible type "object"; expected "A" If your IDE does not understand the types specified in the strings, then you may be using an outdated version. The proposal to use strings in this case was added to PEP 384 by the creator of the Python language (by the way, he is also a lawyer introducing type hints to Python) 2- import m vs. from m import n certainly matters (c from m import n you just get ImportError) - at least you would try the code before commenting. - jfs
    • Well, that's right. You import the desired name, and then specify it in the tooltip. Even if it will be specified inside the string, it still needs to be imported. - Mr Fix
     def modCreator(): print(''' class A: def __init__(self): from mod1 import B self._B = B(self)''', file=open('mod2.py', 'w')) print(''' from mod2 import A class B: def __init__(self, arg: A): self._a = arg''', file=open('mod1.py', 'w')) if __name__ == '__main__': modCreator() from mod1 import B from mod2 import A a = A() b = B(A) print(a, a._B) print(b, b._a) 

    out:

     <mod2.A object at 0x01121ED0> <mod1.B object at 0x01121EB0> <mod1.B object at 0x01121F30> <class 'mod2.A'> 
    • What is this??? - Mr Fix
    • this is the solution to the cross-reference problem in your example - vadim vaduxa
    • Well, not quite. Although it is possible that the only thing. No other solutions for sure? - Mr Fix
    • Why was it output to files through prints? Why was it even necessary to output to files, why was it impossible to just write code? - Mr Fix
    • Why so many questions? and yes, there are definitely other solutions - vadim vaduxa
     def modCreate(): modules = {} modules['A.py'] = ''' from G import glob class A: def __init__(self, x: glob.B): self.x = x self.B = glob.B(self) glob.A = A ''' modules['B.py'] = ''' from G import glob class B: def __init__(self, x: glob.A): self.x = x glob.B = B ''' modules['G.py'] = ''' class Glob: def __getattr__(self, class_: object): print('skip %s' % class_) glob = Glob() ''' for module, code in modules.items(): print(code, file=open(module, 'w')) if __name__ == '__main__': modCreate() from B import B from A import A from G import glob print(glob, [[c, getattr(glob, c)] for c in dir(glob) if not c.startswith('_')]) a = A(B) print(a, ax, aB) b = B(A) print(b, bx) print(A == glob.A) 

    out:

     skip A <G.Glob object at 0x02ACDE10> [['A', <class 'A.A'>], ['B', <class 'B.B'>]] <AA object at 0x02AA1F10> <class 'B.B'> <BB object at 0x02AA1A50> <BB object at 0x02AA1F90> <class 'A.A'> True 
    • In principle - this is of course a trick. Very curious trick. But I'm not sure that it makes sense to do this in practice, as the code may be confused. Local imports seem to me less evil. - Mr Fix
    • Mark that the answer is correct or not? - Mr Fix
    • @MisterFix, it makes sense to mark if the answer suits you. If not sure, then wait for another answer. - insolor