如何加载给定完整路径的Python模块?

请注意,文件可以位于文件系统中用户具有访问权限的任何位置。


另请参阅:如何导入以字符串形式命名的模块?


当前回答

我给你做了一个用小鬼的包裹。我称之为import_file,它的用法如下:

>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')

您可以通过以下方式获取:

http://pypi.python.org/pypi/import_file

或在

http://code.google.com/p/import-file/

其他回答

可以使用__import__和chdir执行此操作:

def import_file(full_path_to_module):
    try:
        import os
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)
        save_cwd = os.getcwd()
        os.chdir(module_dir)
        module_obj = __import__(module_name)
        module_obj.__file__ = full_path_to_module
        globals()[module_name] = module_obj
        os.chdir(save_cwd)
    except Exception as e:
        raise ImportError(e)
    return module_obj


import_file('/home/somebody/somemodule.py')

您可以使用

load_source(module_name, path_to_file)

方法。

这个答案是对Sebastian Rittau对评论的回答的补充:“但是如果你没有模块名怎么办?”这是一种快速而肮脏的方法,可以将可能的Python模块名指定为文件名——它只是沿着树向上移动,直到找到一个没有__init__.py文件的目录,然后将其转换回文件名。对于Python 3.4+(使用pathlib),这是有意义的,因为Python 2可以使用“imp”或其他方式进行相对导入:

import pathlib

def likely_python_module(filename):
    '''
    Given a filename or Path, return the "likely" python module name.  That is, iterate
    the parent directories until it doesn't contain an __init__.py file.

    :rtype: str
    '''
    p = pathlib.Path(filename).resolve()
    paths = []
    if p.name != '__init__.py':
        paths.append(p.stem)
    while True:
        p = p.parent
        if not p:
            break
        if not p.is_dir():
            break

        inits = [f for f in p.iterdir() if f.name == '__init__.py']
        if not inits:
            break

        paths.append(p.stem)

    return '.'.join(reversed(paths))

当然有改进的可能性,可选的__init__.py文件可能需要进行其他更改,但如果您通常有__init__.pry,这就有了窍门。

下面是一些适用于所有Python版本的代码,从2.7-3.5到其他版本。

config_file = "/tmp/config.py"
with open(config_file) as f:
    code = compile(f.read(), config_file, 'exec')
    exec(code, globals(), locals())

我测试了它。它可能很难看,但到目前为止,它是唯一一个适用于所有版本的。

这是我的两个仅使用pathlib的实用程序函数。它从路径推断模块名称。

默认情况下,它从文件夹中递归加载所有Python文件,并用父文件夹名替换init.py。但您也可以提供路径和/或glob来选择某些特定文件。

from pathlib import Path
from importlib.util import spec_from_file_location, module_from_spec
from typing import Optional


def get_module_from_path(path: Path, relative_to: Optional[Path] = None):
    if not relative_to:
        relative_to = Path.cwd()

    abs_path = path.absolute()
    relative_path = abs_path.relative_to(relative_to.absolute())
    if relative_path.name == "__init__.py":
        relative_path = relative_path.parent
    module_name = ".".join(relative_path.with_suffix("").parts)
    mod = module_from_spec(spec_from_file_location(module_name, path))
    return mod


def get_modules_from_folder(folder: Optional[Path] = None, glob_str: str = "*/**/*.py"):
    if not folder:
        folder = Path(".")

    mod_list = []
    for file_path in sorted(folder.glob(glob_str)):
        mod_list.append(get_module_from_path(file_path))

    return mod_list