Import a library which is in a sibling of the current folder

不羁的心 提交于 2020-06-24 12:06:06

问题


With the folder structure

lib/
    abcd/
        __init.py__
        lib.py
app.py

the code

from lib.abcd import lib

works. But with this file structure:

bin/
    app.py
lib/
    abcd/
        __init.py__
        lib.py

the code

from ..lib.abcd import lib     

gives an import error.

How to do the import properly when the library is in a sibling of the current folder? (or subfolder of a sibling folder)

I know that there might some hack that involves adding lib/ to the PATH, but is there an elegant Pythonic solution?

If not, is there a real internal reason to prevent users to do this simple import in a simple way?


回答1:


Methods to do this


Method #1: Using the sys module: You can easily accomplish what you are trying to do using the sys module. To import the lib package, you can use one of the two codes listed below:

import sys
sys.path.append('<PATH_TO_LIB_FOLDER>')

from lib.abcd import lib

or

import sys
sys.path.insert(0, '<PATH_TO_LIB_FOLDER>')

Method #2: Using the os module: Another method is to use the os module. Here is an example code that imports the lib module using the os module by invoking the os.path.join method:

import os

path = os.path.join("<PATH>/lib/abcd", "lib")
from lib.abcd import lib

Method #3: Add the module to your PYTHONPATH: This is not the best method in most cases, but if you don't want to keep using the sys or os module to import lib, this is ideal. All you have to do is type this in your bash terminal:

export PYTHONPATH=<PATH_TO_LIB> python lib.py

Then in your python shell you can import it like this:

from lib.abcd import lib

Method #4: Combine the sys and os module (recommended): This is the most efficient method and will save you a lot of time. This code combines the os and sys module like this:

import sys, os
sys.path.append(os.path.abspath(os.path.join('..', 'lib')))

Then you can import your module with ease like this:

from lib.abcd import lib

How all the codes work:

All the codes above are very simple. All the examples except for "Method #3", add your module to the PYTHONPATH temporarily. "Method #3" on the other hand, adds the module to your PYTHONPATH permanently.




回答2:


Surface level look

Normally, you can't. When importing a file, Python only searches the current directory, the directory that the entry-point script is running from, and sys.path which includes locations such as the package installation directory (it's actually a little more complex than this, but this covers most cases).

The reason you don't see this problem for importing installed modules is because they are installed in a location that is already on your path, or the location is added to the path by the installing utility (pip for example).

You can add to the Python path at runtime:

    import sys
    sys.path.insert(0, '../lib')

    import file  

You can also use sys.path.append('../lib'), but then it will be searched for last in your path and may be overridden by previous path entries.

I have read through the import documentation extensively, and as far as I can tell, the answer to your question is no, there is no way to do this in a purely "Pythonic" way.

More in depth look:

Looking deeper into the import documentation explains this:

The import statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope. The search operation of the import statement is defined as a call to the __import__() function, with the appropriate arguments. The return value of __import__() is used to perform the name binding operation of the import statement.

Looking more closely at __import__:

__import__(name, globals=None, locals=None, fromlist=(), level=0)

Note: This is an advanced function that is not needed in everyday Python programming, unlike importlib.import_module().

This function is invoked by the import statement. It can be replaced (by importing the builtins module and assigning to builtins.__import__) in order to change semantics of the import statement, but doing so is strongly discouraged as it is usually simpler to use import hooks (see PEP 302) to attain the same goals and does not cause issues with code which assumes the default import implementation is in use. Direct use of __import__() is also discouraged in favor of importlib.import_module().

level specifies whether to use absolute or relative imports. 0 (the default) means only perform absolute imports. Positive values for level indicate the number of parent directories to search relative to the directory of the module calling __import__() (see PEP 328 for the details).

This makes me think that you can specify a level in some way that may make the import automatically look on the parent path. I'm guessing that when import is called from your app.py that is not in it's own directory, level is set to 0 and it searches the same level and deeper.

When you call import in app.py from a subfolder, level is still set to 0 and thus can't find the other directories above it. I am investigating a "Pythonic" way of setting this level to 1 when running your script which would appear to fix this problem.



来源:https://stackoverflow.com/questions/54622024/import-a-library-which-is-in-a-sibling-of-the-current-folder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!