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

2025-02-25 09:07:00
admin
原创
23
摘要:问题描述:如何在Python 3.3+.py中导入任意 python 源文件(其文件名可以包含任何字符,并且并不总是以 结尾)?我的使用方法imp.load_module如下:>>> import imp >>> path = '/tmp/a-b.txt' >>...

问题描述:

如何在Python 3.3+.py中导入任意 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'>

它在 Python 3.3 中仍然有效,但是根据imp.load_module文档,它已被弃用:

自 3.3 版起已弃用:不需要,因为应该使用加载器来加载模块,并且 find_module() 已被弃用。

模块imp文档建议使用importlib

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

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


解决方案 1:

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'>

注意:仅适用于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'>

解决方案 2:

针对 Python >= 3.8 进行了更新:

简短版本:

>>> # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
>>> import importlib.util, sys
>>> spec = importlib.util.spec_from_file_location(modname, fname)
>>> module = importlib.util.module_from_spec(spec)
>>> sys.modules[modname] = module
>>> spec.loader.exec_module(module)

完整版本:

>>> import importlib.util
>>> import sys
>>> from pathlib import Path
>>> from typing import TYPE_CHECKING
>>> 
>>> 
>>> if TYPE_CHECKING:
...     import types
...
...
>>> def import_source_file(fname: str | Path, modname: str) -> "types.ModuleType":
...     """
...     Import a Python source file and return the loaded module.

...     Args:
...         fname: The full path to the source file.  It may container characters like `.`
...             or `-`.
...         modname: The name for the loaded module.  It may contain `.` and even characters
...             that would normally not be allowed (e.g., `-`).
...     Return:
...         The imported module

...     Raises:
...         ImportError: If the file cannot be imported (e.g, if it's not a `.py` file or if
...             it does not exist).
...         Exception: Any exception that is raised while executing the module (e.g.,
...             :exc:`SyntaxError).  These are errors made by the author of the module!
...     """
...     # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
...     spec = importlib.util.spec_from_file_location(modname, fname)
...     if spec is None:
...         raise ImportError(f"Could not load spec for module '{modname}' at: {fname}")
...     module = importlib.util.module_from_spec(spec)
...     sys.modules[modname] = module
...     try:
...         spec.loader.exec_module(module)
...     except FileNotFoundError as e:
...         raise ImportError(f"{e.strerror}: {fname}") from e
...     return module
...
>>> import_source_file(Path("/tmp/my_mod.py"), "my_mod")
<module 'my_mod' from '/tmp/my_mod.py'>

Python 3.5 和 3.6 的原始答案

@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 对其进行了测试。

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

解决方案 3:

与@falsetru 类似,但适用于Python 3.5+,并考虑了文档importlib中关于使用importlib.util.module_from_specover 的规定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()

解决方案 4:

importlib辅助函数已在 Python 3.10 上测试

这里有一个方便的、随时可用的助手来替换,并附有示例。该技术与https://stackoverflow.com/a/19011259/895245imp相同,这只是提供了一个更方便的功能。

主程序

#!/usr/bin/env python3

import os
from importlib import util, machinery

def import_path(path):
    module_name = os.path.basename(path).replace('-', '_')
    spec = util.spec_from_loader(
        module_name,
        importlib.machinery.SourceFileLoader(module_name, path)
    )
    module = 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 可执行文件没有扩展名,其中有连字符,如my-cmd。这不是强制性的,但可以产生更好的模块名称,如my_cmd

有关的:

  • 如何根据完整路径动态导入模块?

Python 3.7

在那个版本中,我改为使用以下调用来帮助程序,但它们似乎不适用于 Python 3.10

import importlib    
    spec = importlib.util.spec_from_loader(
    module = importlib.util.module_from_spec(

文档中也提到了这种模式: 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

我不知道如何关闭它,有人问过这个问题:

  • imp 模块已弃用

  • 如何忽略 Python 中的弃用警告

解决方案 5:

经过许多失败的解决方案后,这个对我有用

def _import(func,*args):
    import os
    from importlib import util
    module_name = "my_module"
    BASE_DIR = "wanted module directory path"
    path =  os.path.join(BASE_DIR,module_name)
    spec = util.spec_from_file_location(func, path)
    mod = util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return getattr(mod,func)(*args)

要调用它,只需写下函数名称及其参数_import("function",*args)

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1344  
  随着房地产行业的快速发展,项目管理在确保项目按时、按预算和高质量完成方面变得至关重要。2025年,房地产项目经理将面临更加复杂的项目需求和更高的管理标准。为了应对这些挑战,选择合适的管理工具显得尤为重要。本文将推荐10款在2025年备受房地产项目经理青睐的管理工具,帮助您提升项目管理效率,确保项目成功。禅道项目管理软件...
开源项目管理工具   0  
  在全球化日益深入的今天,跨国团队的合作已成为企业运营的常态。随着团队成员分布在不同国家和地区,语言和文化的多样性给项目管理带来了新的挑战。为了确保项目顺利进行,选择一款适合的多语言项目管理工具至关重要。本文将介绍2025年跨国团队必备的10款多语言项目管理工具,帮助团队高效协作,提升项目成功率。禅道项目管理软件禅道项目...
项目管理平台   0  
  随着工程管理领域的不断发展,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的重要工具。2025年,工程管理将面临更加复杂的挑战,包括跨团队协作、资源优化、数据驱动决策等。因此,选择一款适合的项目管理软件显得尤为重要。本文将为您推荐2025年最适合工程管理的10款项目管理软件,帮助您在众多选择中找到最适合...
项目管理工具   0  
  跨文化团队的项目运作日益普遍,高级项目管理师在其中扮演着至关重要的角色。有效的沟通是确保项目顺利推进、团队协作无间的关键因素。高级项目管理师需要掌握一系列独特的沟通技巧,以跨越文化差异带来的障碍,实现高效的信息传递与团队协作。理解文化差异文化差异是跨文化团队沟通中面临的首要挑战。不同国家、地区和民族有着各自独特的价值观...
项目管理包括哪些内容   1  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用