Do all dynamic languages have the circular import issue?
Asked Answered
P

5

5

For the following Python code:

first.py

# first.py
from second import Second

class First:
    def __init__(self):
        print 'Second'

second.py

# second.py
from first import First

class Second:
    def __init__(self):
        print 'Second'

After creating the files and running the following from the shell:

python first.py

I get the error: ImportError: cannot import name Second

Do other dynamic languages like Ruby have this kind of issue? The reason I'm asking is because I'm encountering this issue in a Django project where 2 models depend on each other. I know that the possible solutions are re-designing the project or import on demand. I just want to know if devs in other dynamic languages have experienced this issue.

Pinkiepinkish answered 17/2, 2010 at 22:38 Comment(3)
You should always subclass object rather than nothing so that you are using new-style classes.Hauberk
@Mike, he did not specify his version of Python.Waterhouse
@Hamish Grubijan, i) He used the print statement, which is gone in Python 3, ii) He said he was using Django, which does not support Python 3, and iii) No one is using Python 3; Python 2 is the reasonable default assumption.Hauberk
H
13

Python can handle circular imports to some extent. In cases where no sense can be made, the solution would probably still not make sense in another language. Most of the problems can be cleared up by using import first and later referring to first.First instead of from first import First.

It would be better if you could move shared code off to its own module or somehow refactor out the need for a circular import. Circular imports always indicate a design problem.

Hauberk answered 17/2, 2010 at 22:48 Comment(3)
So if I want to split off thousands of lines of code into multiple files, it's a design problem?Fantasize
When you split code from one file into multiple files which rely on circular imports, you've not done it with an optimal design.Hauberk
Circular imports don't necessarily mean a design problem. For example, you want to put each model class in its own file/module. But they have relationships between the models. There is no design problem there, there is a necessity for circular dependencies. In Rails though, they hack it and you specify them as strings instead of the objects, then internally on the next frame they are wired together.Rufus
L
4

Recursive definitions are not a problem restricted to dynamic languages. It's also often an issue in statically types languages. It might show up as a compile error because one of the types will be used before it is defined.

In some languages the solution is to use forward declarations. Other languages solve the problem by compiling more than one file at once.

In Python you can solve the problem by moving the imports from the top level into the functions where they are needed. Also, a circular reference isn't actually an error, so if you are careful, you can make it work anyway.

Loch answered 17/2, 2010 at 22:48 Comment(0)
S
3

All other posters are correct that circular imports are a serious issue that you should fix, structurally.

However, specificly with Python/Django models, you can use string names to setup foreign keys to avoid these circular dependency issues --

#appA/models.py
class A(models.Model):
  b = models.ForeignKey('appB.b')

#appB/models.py
class B(models.Model):
  a = models.ForeignKey('appA.a')

Circular references in DB tables aren't necessarily a bad thing (but aren't always good); Django allows for definition of keys with a string to help in situations where it's necessary. If you actually need to instantiate the two classes inside one another, you've got bigger problems.

Sylvanus answered 17/2, 2010 at 22:57 Comment(0)
S
1

Logically this is a paradox. It's the chicken and egg issue in code form. One of them has to come first. As suggested by the others, please go back to the drawing board and you'll be better off for it in the long run. Languages prevent you from doing this stuff for a reason!

Samadhi answered 17/2, 2010 at 22:52 Comment(5)
It might be a bad idea, but it isn't a paradox to have two modules depend on each other.Lydgate
To depend on each other incrementally, perhaps, but distinctly as suggested in the OP? Paradox.Samadhi
No it's not. Haven't you ever heard of recursion?Fantasize
Recursive functions, absolutely. Recursive modules? Never.Samadhi
if two functions in different files recursively call each other, you will get this problem. The solution is to import one from inside the other. Just because two functions are in different files, doesn't mean that they are in different modules. It just means that they are somehow logically separate, or because combining them would make a file that is too large.Fantasize
G
1

Note that if you just move your imports to the end of your module, circular imports will work as expected. Like so:

first.py

# first.py
class First:
  def __init__(self):
    print 'Second'
from second import Second

second.py

# second.py
class Second:
    def __init__(self):
        print 'Second'
from first import First

Fredrik Lundh's import reference is worth a read. As others have advised, though, you're best off rejiggering your code to avoid circular imports entirely.

Gman answered 18/2, 2010 at 1:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.