导入任意python源文件。 (Python 3.3+)

时间:2013-09-25 16:07:51

标签: python python-3.x python-3.3

如何在 Python 3.3 + 中导入任意python源文件(其文件名可能包含任何字符,并不总是以.py结尾)?

我使用imp.load_module如下:

>>> import imp
>>> path = '/tmp/a-b.txt'
>>> with open(path, 'U') as f:
...     mod = imp.load_module('a_b', f, path, ('.py', 'U', imp.PY_SOURCE))
...
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>

它仍适用于Python 3.3,但根据imp.load_module文档,它已被弃用:

  

从版本3.3开始不推荐使用:不需要使用加载器   不推荐使用load modules和find_module()。

imp模块文档建议使用importlib

  

注意新程序应该使用importlib而不是此模块。

在不使用已弃用的imp.load_module函数的情况下,在Python 3.3+中加载任意python源文件的正确方法是什么?

4 个答案:

答案 0 :(得分:49)

找到importlib test code的解决方案。

使用importlib.machinery.SourceFileLoader

>>> import importlib.machinery
>>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
>>> mod = loader.load_module()
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>

注意:仅适用于 Python 3.3 +

自Python 3.4起,不推荐使用

UPDATE Loader.load_module。请改用Loader.exec_module

>>> import types
>>> import importlib.machinery
>>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
>>> mod = types.ModuleType(loader.name)
>>> loader.exec_module(mod)
>>> mod
<module 'a_b'>

>>> import importlib.machinery
>>> import importlib.util
>>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
>>> spec = importlib.util.spec_from_loader(loader.name, loader)
>>> mod = importlib.util.module_from_spec(spec)
>>> loader.exec_module(mod)
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>

答案 1 :(得分:16)

@falsetru解决方案的缩短版本:

>>> import importlib.util
>>> spec = importlib.util.spec_from_file_location('a_b', '/tmp/a-b.py')
>>> mod = importlib.util.module_from_spec(spec)
>>> spec.loader.exec_module(mod)
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>

我用Python 3.5和3.6进行了测试。

根据评论,它不适用于任意文件扩展名。

答案 2 :(得分:6)

类似于@falsetru,但对于 Python 3.5 + ,并说明the importlib docimportlib.util.module_from_spec上使用types.ModuleType的状态:

  

此功能[importlib.util.module_from_spec优于使用types.ModuleType创建新功能   规格模块用于设置多个导入控制的属性   该模块。

通过修改importlib列表,我们可以单独使用importlib.machinery.SOURCE_SUFFIXES导入任何文件。

import importlib

importlib.machinery.SOURCE_SUFFIXES.append('') # empty string to allow any file
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# if desired: importlib.machinery.SOURCE_SUFFIXES.pop()

答案 3 :(得分:3)

importlib辅助功能

这里是一个方便的,随时可用的帮助程序,以示例代替imp。该技术与https://stackoverflow.com/a/19011259/895245相同,只是提供了更方便的功能。

main.py

#!/usr/bin/env python3

import os
import importlib

def import_path(path):
    module_name = os.path.basename(path).replace('-', '_')
    spec = importlib.util.spec_from_loader(
        module_name,
        importlib.machinery.SourceFileLoader(module_name, path)
    )
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    sys.modules[module_name] = module
    return module

notmain = import_path('not-main')
print(notmain)
print(notmain.x)

非主要

x = 1

运行:

python3 main.py

输出:

<module 'not_main' from 'not-main'>
1

我将-替换为_,因为我的不带扩展名的可导入Python可执行文件带有连字符。这不是强制性的,但会产生更好的模块名称。

https://docs.python.org/3.7/library/importlib.html#importing-a-source-file-directly

中的文档中也提到了这种模式

我最终选择了它,因为更新到Python 3.7后,import imp打印:

DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses

我不知道如何关闭它,有人问到:

在Python 3.7.3中进行了测试。