Understanding A Chain of Imports in Python
I know there are several similar questions, but I'm struggling to understand the error I'm getting and browsing the docs and similar questions hasn't helped yet. If anything, the similar questions make me feel like what I'm doing is right.
I have the following files:
src/main.py
from pack import pack
if __name__ == '__main__':
pack.exec("Hello Universe!")
src/pack/pack.py
import util
def exec(text):
util.write(text)
if __name__ == '__main__':
exec("Hello World!")
src/pack/util.py
def write(text):
print(text)
*src/pack/_init_.py*
EMPTY FILE
When I run python pack.py
from the src/pack
directory, it works (prints "Hello World!"). However when I run python main.py
from the src
directory I get the following exception:
Traceback (most recent call last):
File ".../src/main.py", line 1, in <module>
from pack import pack
File ".../src/pack/pack.py", line 1, in <module>
import util
ImportError: No module named util
If I change the import line in pack.py
to from . import util
as suggested, effectively the opposite occours. main.py
runs successfully, however now pack.py
fails, raising:
Traceback (most recent call last):
File ".../src/pack/pack.py", line 1, in <module>
from . import util
ValueError: Attempted relative import in non-package
I would hav开发者_JS百科e thought that imports are relative to the current location, and as such you ought to be able to construct a chain of imports like this. It seems very odd to me that module is supposed to import a sibling file differently depending on where the program starts.
Can someone explain why this error occurs in one way but not the other, and if there is some way to allow this file structure to run whether I want to run from main.py
or pack.py
?
You will have trouble making the import work in both cases. This is because in one case you are running pack.py as the main file and in another you run it as part of a package.
When you run it as a standalone script python pack.py
, the "pack" directory gets added to the PYTHONPATH, meaning that you can import any module in it. Hence, import util
will work.
When you run python main.py
you add the src directory to your PYTHONPATH. This means that any module or package in src
, for example the pack
directory, now becomes importable. Hence from pack import pack
. However, to access util.py
you now need to do from pack import util
. You can also do from . import util
from within pack.py
, as you noticed.
But you can't really do both at the same time. Either src/
is the main directory or src/pack
is.
The obvious, but wrong solution, is to let the main.py add the src/pack
directory to the PYTHONPATH. That will work, but it's not a good idea. The correct way to do this is to make up your mind. Is src/pack
a module that should be imported via import pack
or is it just a folder with a bunch of Python scripts? Decide! :-)
I think in this case its' obvious that src/pack
should be a module. So then treat it like a module, and make sure it's available like a module. Then you can from pack import util
even when running pack.py
as a main script.
How do you do that? Well, basically you either install the pack module in your site-packages, or you add the src
directory to the PYTHONPATH. That last one is what you want during development. You can either do it manually with export PYTHONPATH=<path>
or you can let your testrunners do it for you. You don't have a testrunner? Well you should, but that's another question. :)
For installing it permanently once you don't do development anymore, take a look at Distribute. It includes a testrunner. ;)
Your link is to python 2.7 documentation, but it looks like you are using Python 3.x.
See here: http://docs.python.org/py3k/ for the correct docs.
Python 3 removes implicit relative import. That is, you cannot import packages within the same module in the same way anymore.
You need to use from . import util
, this is an explicitly relative import and is allowed. import X
no longer checks the current directory. Instead it only check the entries in sys.path. This includes the directory of the script was started and python's standard library.
You are missing __init__.py in your pack directory.
Create an empty file called __init__.py in your pack directory.
Nothing in the correct documentation supports your theory that this form of importing should work.
精彩评论