Are imports supposed to be like this?

Chris Angelico rosuav at gmail.com
Mon May 9 20:20:33 EDT 2016


On Tue, May 10, 2016 at 9:54 AM, Brendan Abel <007brendan at gmail.com> wrote:
> Consider the following example python package where `a.py` and `b.py`
> depend on each other:
>
>     /package
>         __init__.py
>         a.py
>         b.py
>
>
> There are several ways I could import the "a.py" module in "b.py"
>
>     import package.a           # Absolute import
>     import package.a as a_mod  # Absolute import bound to different name
>     from package import a      # Alternate absolute import
>     import a                   # Implicit relative import (deprecated, py2
> only)
>     from . import a            # Explicit relative import
>
> Unfortunately, only the 1st and 4th syntax actually work when you have
> circular dependencies (the rest all raise `ImportError` or
> `AttributeError`), and the 4th syntax only works in python 2 and is
> generally discouraged because of the possibility of name conflicts.

The fifth is the one that I would recommend. Can you give an example
of a circular dependency that makes it fail? Here's the trivial case
that I tried:

rosuav at sikorsky:~/tmp$ mkdir package
rosuav at sikorsky:~/tmp$ touch package/__init__.py
rosuav at sikorsky:~/tmp$ cat >package/a.py
from . import b
def func_a(x):
    print("func_a: %d" % x)
    if x % 2: b.func_b(x-1)
rosuav at sikorsky:~/tmp$ cat >package/b.py
from . import a
def func_b(x):
    print("func_b: %d" % x)
    if x % 2: a.func_a(x-1)
rosuav at sikorsky:~/tmp$ python3
Python 3.6.0a0 (default:98678738b7e9, May  2 2016, 13:37:04)
[GCC 5.3.1 20160409] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import package.a
>>> package.a.func_a(5)
func_a: 5
func_b: 4
>>> import package.b
>>> package.b.func_b(5)
func_b: 5
func_a: 4
>>>

The imports work fine as long as you use strictly "from . import
modulename", and not "from .modulename import objectname". Circular
imports of the latter form will indeed fail, but if you have
non-circular imports, you can stop them from failing by forcing module
load order:

rosuav at sikorsky:~/tmp$ cat package/a.py
from .b import func_b
def func_a(x):
    print("func_a: %d" % x)
    if x % 2: func_b(x-1)
rosuav at sikorsky:~/tmp$ cat >package/__init__.py
from . import a
from . import b


This does prevent lazy loading, though, so if your circular imports
are in an optional part of the package (and one that takes a long time
to load), you might want to consider setting it up some other way.

ChrisA



More information about the Python-list mailing list