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

fal*_*tru 54 python python-3.x python-3.3

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

我使用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'>
Run Code Online (Sandbox Code Playgroud)

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

从版本3.3开始不推荐使用:不需要使用加载器来加载模块,并且不推荐使用find_module().

imp模块文档建议使用importlib:

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

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

fal*_*tru 53

importlib测试代码中找到解决方案.

使用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'>
Run Code Online (Sandbox Code Playgroud)

注意:仅适用于Python 3.3+.

Loader.load_module自Python 3.4起,不推荐使用UPDATE.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'>
Run Code Online (Sandbox Code Playgroud)
>>> 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'>
Run Code Online (Sandbox Code Playgroud)

  • Downvoter:我怎样才能改进答案?如果你有更好的方法来完成我想要的,请告诉我. (21认同)
  • 有一个有用的警告:`load_module`通过`warnings.catch_warnings`忽略.如果您改为使用`mod = imp.load_source('a_b','/ tmp/ab.txt')`,则会引发以下警告(使用`-Wall`):不推荐使用`DeprecationWarning:imp.load_source(); 使用importlib.machinery.SourceFileLoader(name,pathname).load_module()代替`. (3认同)
  • 底部的第一个示例和第二个示例有什么区别? (2认同)

Ste*_*fke 18

更短版本的@falsetrue解决方案:

>>> 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'>
Run Code Online (Sandbox Code Playgroud)

我用Python 3.5和3.6测试了它.

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

  • `importlib.util.spec_from_file_location(..)` 为我返回 `None`;导致以下 `importlib.util.module_from_spec(..)` 调用异常。(见 https://i.imgur.com/ZjyFhif.png) (3认同)
  • `importlib.util.spec_from_file_location`适用于已知文件扩展名(`.py`,`.so`,..),但不是为别人(`.txt` ...) (3认同)

awa*_*lll 8

类似于@falsetru但是对于Python 3.5+并且考虑importlibdoc使用importlib.util.module_from_spec的内容types.ModuleType:

此函数[ importlib.util.module_from_spec]优于使用types.ModuleType创建新模块,因为spec用于在模块上设置尽可能多的导入控制属性.

我们可以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()
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 6

importlib 辅助功能

这是一个方便的、随时可用的帮助器来替换imp,并带有示例。技术与/sf/answers/1330788161/相同,只是提供了一个更方便的功能。

主文件

#!/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)
Run Code Online (Sandbox Code Playgroud)

非主要

x = 1
Run Code Online (Sandbox Code Playgroud)

跑:

python3 main.py
Run Code Online (Sandbox Code Playgroud)

输出:

<module 'not_main' from 'not-main'>
1
Run Code Online (Sandbox Code Playgroud)

我替换-_因为我没有扩展名的可导入 Python 可执行文件有连字符,如my-cmd. 这不是强制性的,但会产生更好的模块名称,例如my_cmd.

这种模式也在文档中提到: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
Run Code Online (Sandbox Code Playgroud)

我不知道如何关闭它,这是在以下位置询问的:

在 Python 3.7.3 中测试。