简体   繁体   中英

Relative imports in python 2.5

I know that there are lots of questions about the same import issues in Python but it seems that nobody managed to provide a clear example of correct usage.

Let's say that we have a package mypackage with two modules foo and bar . Inside foo we need to be able to access bar .

Because we are still developing it, mypackage is not in sys.path .

We want to be able to:

  • import mypackage.foo
  • run foo.py as a script and execute the sample usage or tests from the __main__ section.
  • use Python 2.5

How do we have to do the import in foo.py in order to be sure it will work in all these cases.

# mypackage/__init__.py
...

# mypackage/foo/__init__.py
...

# mypackage/bar.py  
def doBar()
    print("doBar")

# mypackage/foo/foo.py
import bar # fails with module not found
import .bar #fails due to ValueError: Attempted relative import in non-package

def doFoo():
    print(doBar())

if __name__ == '__main__':
    doFoo()

Take a look at the following info from PEP 328 :

Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (eg it is set to '__main__' ) then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

When you run foo.py as a script, that module's __name__ is '__main__' , so you cannot do relative imports. This would be true even if mypackage was on sys.path . Basically, you can only do relative imports from a module if that module was imported.

Here are a couple of options for working around this:

1) In foo.py , check if __name__ == '__main__' and conditionally add mypackage to sys.path :

if __name__ == '__main__':
    import os, sys
    # get an absolute path to the directory that contains mypackage
    foo_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
    sys.path.append(os.path.normpath(os.path.join(foo_dir, '..', '..')))
    from mypackage import bar
else:
    from .. import bar

2) Always import bar using from mypackage import bar , and execute foo.py in such a way that mypackage is visible automatically:

$ cd <path containing mypackage>
$ python -m mypackage.foo.foo

My solution looks a bit cleaner and can go at the top, with all the other imports:

try:
   from foo import FooClass
except ModuleNotFoundError:
   from .foo import FooClass

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM