如何将目录导入为python模块

Kil*_*han 4 python python-import python-3.x

如果有目录/home/project/aaa。我知道它是一个 Python 包。

那么,我如何通过知道它的路径来导入这个模块。

意味着,我希望代码有效:

aaa = load_module("/home/project/aaa")
Run Code Online (Sandbox Code Playgroud)

我知道的唯一方法是,添加/home/projectsys.path

但它可能会引起一些问题:

如果我添加/home/projectsys.path

如果pytest路径中有一个目录/home/project

那么官方的 pytest 包将不起作用。

我试试吧importlib。但似乎importlib只能将文件作为模块导入,而不是路径。

所以,我试试看:

aaa = importlib.import_module("aaa", "/home/project")
Run Code Online (Sandbox Code Playgroud)

或者

aaa = importlib.import_module("aaa", "/home/project/aaa")
Run Code Online (Sandbox Code Playgroud)

他们两个都不工作。

那么,有没有其他方法可以做我想做的事?

哦,我正在使用 Python3.6


更新于 20170628

(注意:有一个__init__.pyin 文件夹/home/project/aaa

我知道的所有解决方案都是从单个文件导入模块。

如果bbb.py文件夹中有文件/home/project/aaa

然后,添加/home/project/aaasys.path__path__(无论如何)

import bbb 工作,但不是 import aaa

我想问的是如何将文件夹(或目录)作为模块导入。

对于我的示例,文件夹aaa被视为一个模块。

我想用import aaaimport {SOMETHING IN AAA}

a_g*_*est 7

以下是完成该任务的五种不同方式。

对于以下考虑,我参考 Python 3.5+。


注册自定义 Finder

Python在导入模块时使用查找器。如果 finder 知道如何处理特定请求的模块,则它返回相应的模块规范,否则返回None. Python 已经注册了三个不同的查找器,可以在以下位置找到sys.meta_path

>>> import sys
>>> sys.meta_path
[<class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib_external.PathFinder'>]
Run Code Online (Sandbox Code Playgroud)

第一个处理内置模块,第二个处理冻结模块(某种“自包含”Python 脚本,请参阅wiki),最后一个处理可以在sys.path. 因此,如果我们sys.path通过附加'/home/project'进行修改,那么将是此查找器提供相应的规范。

sys.path我们可以注册我们自己的使用以下功能的查找器,而不是修改PathFinder

import importlib.machinery

class CustomFinder(importlib.machinery.PathFinder):
    _path = ['/home/project']

    @classmethod
    def find_spec(cls, fullname, path=None, target=None):
        return super().find_spec(fullname, cls._path, target)
Run Code Online (Sandbox Code Playgroud)

在这里,我们明确地告诉导入模块时PathFinder要查看/home/project

我们可以按如下方式注册查找器:

import sys
sys.meta_path.append(CustomFinder)
Run Code Online (Sandbox Code Playgroud)

然后我们可以导入aaa将通过以下方式找到的包CustomFinder

import aaa
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅PEP-302

延长 sys.path

我们可以修改sys.path以将所需的包放在路径上:

import sys

sys.path.append('/home/project')
import aaa
from aaa import whatever
# Optionally remove the added path.
sys.path.pop()
Run Code Online (Sandbox Code Playgroud)

由于导入期间执行的搜索顺序,将此目录附加到路径不会阻止具有相同名称的“现有”(例如内置包)。

添加一个包含一个本地模块 __path__

您可以添加aaa.py包含以下代码的本地模块(实际上您可以将其添加到 Python 路径上的任何位置):

__path__ = ['/home/project/aaa']
Run Code Online (Sandbox Code Playgroud)

然后,您可以执行import将引用您使用__path__变量引用的包的语句:

from aaa import whatever
Run Code Online (Sandbox Code Playgroud)

如果您愿意,import aaa可以通过在目录层次结构中上一级应用相同的方法来模拟这一点。project.py使用以下代码添加本地模块(例如):

__path__ = ['/home/project']
Run Code Online (Sandbox Code Playgroud)

然后你可以做

from project import aaa
Run Code Online (Sandbox Code Playgroud)

import aaa如果aaa在路径上,这非常相似(前提是没有其他命名的模块project在路径上具有优先级)。

创建指向包的符号链接

您可以创建一个指向包目录的符号链接。例如在 Unix 上:

ln -s /home/project/aaa aaa
Run Code Online (Sandbox Code Playgroud)

然后您可以通过 导入包import aaa,假设您在放置符号链接的目录中执行此操作。

符号链接也可以通过以下方式在您的程序中创建

import os

package = '/home/project/aaa'
target = os.path.split(package)[-1]  # For example.
if not os.path.exists(target):
    # `target_is_directory=True` is needed for Windows platform.
    os.symlink(package, target, target_is_directory=True)

# Now import the package.
aaa = __import__(target)
Run Code Online (Sandbox Code Playgroud)

通过安装包 setuptools

您可以添加/home/project/setup.py包含(例如)以下代码的脚本:

from setuptools import setup

setup(
    name='aaa',
    packages=[
        'aaa',
        # Add any sub-packages that `aaa` contains here.
    ]
)
Run Code Online (Sandbox Code Playgroud)

然后你可以通过安装包cd /home && pip install -e project,你可以很容易地将它导入到你的其他 Python 文件中:

import aaa
from aaa import whatever
Run Code Online (Sandbox Code Playgroud)

通过使用,virtualenv您可以将安装的软件包保持在干净的状态。