0

I'm writing a library named Foo for an example.

The __init__.py file:

from .foo_exceptions import *
from .foo_loop import FooLoop()
main_loop = FooLoop()
from .foo_functions import *

__all__ = ['main_loop'] + foo_exceptions.__all__ + foo_functions.__all__

When installed, it can be used like this:

# example A
from Foo import foo_create, main_loop

foo_obj = foo_create()
main_loop().register(foo_obj)

or like this:

# example B
import Foo

foo_obj = Foo.foo_create()
Foo.main_loop().register(foo_obj)

I clearly prefer the example B approach. No name conflicts and the source of each external object is explicitely stated.


So much for introduction, now my question. Inside this library I need to import something from a different file. Again, I have several ways to do it. And the question is which style to prefer - C, D or E? Read below.

# example C
from . import foo_exceptions

raise foo_exceptions.FooError("fail")

or

# example D
from .foo_exceptions import FooError

raise FooError("fail")

or

# example E
from . import FooError

raise FooError("fail")

Approach C has the disadvantage, that importing a whole module instead of importing just a few required objects increases the chance of a cyclical import problem. Also consider this line:

from . import foo_exceptions, main_loop

It looks like an import of 2 symbols from one source, but it isn't. The former (foo_exceptions) is a module (.py file) in the current directory and the latter is an object defined in __init__.py.

That's why I'm not using style C and the question in its final form is: D or E (and why)?

(Thank you for reading this long question. All code fragments are examples only and may contain typos)


After the answer from alexanderlukanin:

  • EDIT1: corrected errors in init.py
  • NOTE1: foo_ prefixes are only to emphasize the relationship between objects

  • EDIT2: When importing an object which is not part of the library interface, style E is not usable. I think we have a winner: It's the from .module import symbol form.
VPfB
  • 14,927
  • 6
  • 41
  • 75

1 Answers1

0
  1. Don't use old-style relative imports:

    # Import from foo/foo_loop.py
    # This DOES NOT WORK in Python 3
    # and MAY NOT WORK AS EXPECTED in Python 2
    from foo_loop import FooLoop
    
    # This is reliable and unambiguous
    from .foo_loop import FooLoop
    
  2. Don't use asterisk import unless you really have to.

    # Namespace pollution! Name clashes!
    from .submodule import *
    
  3. Don't use prefixes - you've got namespaces exactly for that purpose.

    # Unpythonic
    from foo import foo_something_create
    foo_something_create()
    
    # Pythonic
    import foo.something
    foo.something.create()
    
  4. Your package's API must be well-defined. Your implementation must not be too tangled. The rest is a matter of taste.

    # [C] This is good.
    # Import order: __init__.py, exceptions.py
    from . import exceptions
    raise exceptions.FooError
    
    # [D] This is also fine.
    # Import order is the same as above,
    # only name binding inside the current module is different.
    from .exceptions import FooError
    raise FooError
    
    # [E] This is not as good because it adds one unnecessary level of indirection
    # submodule.py -> __init__.py -> exceptions.py
    from . import FooError
    raise FooError
    

See also: Circular (or cyclic) imports in Python

Community
  • 1
  • 1
alexanderlukanin13
  • 4,577
  • 26
  • 29
  • Thank you for your reply. 1: OK, this is obvious in Python3. 2: I do `import *` only when the module was written for it, i.e. it defines `__all__` . Actually in `__init__.py` of a package makes the `import *` sense, doesn't it? 3: I used the prefixes just to make my example code clearer. I do not use them normally, so we are in agreement here; 4: good point against E in my D or E question. Thanks. – VPfB Oct 22 '15 at 10:03
  • Now I see, that your point 1 was a reaction to my `__init__.py` containing errors. I have corrected them. I'm using Python 3 only, it would not work. – VPfB Oct 22 '15 at 10:25