0

Given this simple folder structure

/main.py
/project/a.py
/project/b.py

main.py is executed by the python interpreter and contains a single line, import project.a.

a and b are modules, they need to import each other. A way to achieve this would be

import project.[a|b]

When working with deeper nested folder structures you don't want to write the entire path everytime you use a module e.g.

import project.foo.bar
project.foo.bar.set_flag(project.foo.bar.SUPER)

Both from project import [a|b] and import project.[a|b] as [a|b] result in an import error (when used in both, a and b).

What is different between the standart import syntax and the from or as syntax? Why is only the standart syntax working for mutual imports?

And more importantly, is there a simple and clean way to import modules that allows mutual imports and assigning shorter names to them (ideally the modules basename e.g. bar in the case of project.foo.bar)?

timakro
  • 1,739
  • 1
  • 15
  • 31
  • Please refer to the [circular import question](http://stackoverflow.com/q/744373/180709) and come back with a more detailed error if it doesn't solve your issue. – zopieux Jan 08 '16 at 13:47
  • @Zopieux I know how the import system works and there is no error. I'm just searching for a way to import modules and assign them shorter names when they import each other. – timakro Jan 08 '16 at 13:58
  • i think `import a` and `import b` should be fine – danidee Jan 08 '16 at 14:06
  • @danidee Oldstyle relative imports where deprecated in 3.x https://www.python.org/dev/peps/pep-0328/#timeline – timakro Jan 08 '16 at 14:13
  • i ran into problems using `from module import something` but i guess it's something i should consider now...never knew it was deprecated – danidee Jan 08 '16 at 14:22
  • @danidee: only *implicit* relative imports are deprecated. Explicit ones (`from . import something`) are still 100% supported – Andrea Corbellini Jan 08 '16 at 14:30
  • @danidee Yes thats why I said "oldstyle" these explicit ones were added when the oldstyle ones where deprecated. But you can only use the explicit ones with the `from` and `as` syntax which again leads to my problem. – timakro Jan 08 '16 at 14:36

1 Answers1

1

When you do either import project.a or from project import a, the following happens:

  1. The module object for project.a is placed into sys.modules. This is a dictionary that maps each module name to its module object, so you'll have sys.modules = {..., 'p.a': <module 'p.a' from '.../project/a.py'>, ...}.
  2. The code for the module is executed.
  3. The a attribute is added to project.

Now, here is the difference between import project.a and from project import a:

  • import project.a just looks for sys.modules['project.a']. If it exists, it binds the name project using sys.modules['project'].
  • from project import a looks for sys.modules['project'] and then checks if the project module has an a attribute.

    You can think of from project import a as an equivalent to the following two lines:

    import project.a  # not problematic
    a = project.a     # causes an error
    

That why you are seeing an exception only when doing from project import a: sys.modules['project.a'] exists, but project does not yet have a a attribute.


The quickest solution would be to simply avoid circular imports. But if you can't, then the usual strategies are:

  • Import as late as possible. Suppose that your a.py looks like this:

    from project import b
    
    def something():
        return b.something_else()
    

    Rewrite it as follows:

    def something():
        from project import b
        return b.something_else()
    

    Of course, you would have to repeat imports in all your functions.

  • Use lazy imports. Lazy imports are not standard feature of Python, but you can find many implementations around. They work by using the "import as late as possible" principle, but they add some syntactic sugar to let you write fewer code.

  • Cheat, and use sys.modules, like this:

    import sys
    import project.a
    a = sys.modules['project.a']
    

    Very un-pythonic, but works.

Obviously, whatever solution you choose, you won't be able to access the attributes from a or b until the modules have been fully loaded.

Andrea Corbellini
  • 17,339
  • 3
  • 53
  • 69
  • That's not what I asked for, I'm searching a way to make imports of the `from` form working with "mutual imports" as I call them, e.g. `a` importing `b` and the other way around. – timakro Jan 08 '16 at 14:38
  • @timgame: sorry, but I can't understand. What what are "mutual imports"? Are you referring to circular imports perhaps? If so, what do `from` and `as` have to do with circular imports? – Andrea Corbellini Jan 08 '16 at 14:41
  • Try the setup of my question in python 3.x. I'm basically searching for a way to run this setup without having to write `project.[a|b]` each time. – timakro Jan 08 '16 at 14:43
  • @timgame: oh, ok, now I got it. Your question is about circular imports. I'll rewrite my answer – Andrea Corbellini Jan 08 '16 at 14:45
  • You didn't try the entire setup, you need the `project` folder with `a` and `b` importing each other. You could replace `main.py` by running a interactive interpreter from the folder where `main.py` would be. – timakro Jan 08 '16 at 14:45
  • But why does `import project.[a|b]` work and `from project import [a|b]` does not (with cyclic imports as you call them)? And is there really now way to get this working like with `import project.[a|b]` but with shorter names? (I asked this in my question) – timakro Jan 08 '16 at 14:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100162/discussion-between-timgame-and-andrea-corbellini). – timakro Jan 08 '16 at 15:10
  • Unfortunately my browser crashes when I try to open the chat. – timakro Jan 08 '16 at 15:11
  • I think I understand why the one works and the other one doesn't but I don't want to avoid cyclic imports. I want to do `import project.[a|b]` but assign the result to a shorter name in the namespace, you didn't said if this is possible and how or if it isn't and why. – timakro Jan 08 '16 at 15:14
  • @timgame: I added a few solutions. Did I address all your questions? – Andrea Corbellini Jan 08 '16 at 15:27