python - 如何在运行时复制 python 模块?

标签 python python-c-extension

我需要制作一个套接字模块的副本才能使用它,并为另外一个套接字模块进行猴子补丁并以不同的方式使用它。

这可能吗?

我的意思是真正复制一个模块,即在 runtime 获得相同的结果,就好像我复制了 socketmodule.c 一样,更改了 initsocket( ) 函数到 initmy_socket(),并将其安装为 my_socket 扩展。

最佳答案

你总是可以做一些技巧,比如导入一个模块然后从 sys.modules 中删除它或尝试复制一个模块。但是,Python 已经在其标准库中提供了您想要的内容。

import imp # Standard module to do such things you want to.

# We can import any module including standard ones:
os1=imp.load_module('os1', *imp.find_module('os'))

# Here is another one:
os2=imp.load_module('os2', *imp.find_module('os'))

# This returns True:
id(os1)!=id(os2)

Python3.3+

imp.load_module is deprecated in python3.3+, and recommends the use of importlib

#!/usr/bin/env python3

import sys
import importlib.util

SPEC_OS = importlib.util.find_spec('os')
os1 = importlib.util.module_from_spec(SPEC_OS)
SPEC_OS.loader.exec_module(os1)
sys.modules['os1'] = os1

os2 = importlib.util.module_from_spec(SPEC_OS)
SPEC_OS.loader.exec_module(os2)
sys.modules['os2'] = os2
del SPEC_OS

assert os1 is not os2, \
    "Module `os` instancing failed"

在这里,我们两次导入相同的模块,但作为完全不同的模块对象。如果您检查 sys.modules,您可以看到您输入的两个名称作为 load_module 调用的第一个参数。看看documentation了解详情。

更新:

为了使这种方法的主要区别显而易见,我想让这一点更清楚:当您以这种方式导入同一个模块时,您将在运行时导入的每个其他模块都可以全局访问这两个版本,这正是据我了解,提问者需要。

下面是另一个强调这一点的例子。

这两个语句的作用完全相同:

import my_socket_module as socket_imported

socket_imported = imp.load_module('my_socket_module',
    *imp.find_module('my_socket_module')
)

在第二行,我们重复 'my_socket_module' 字符串两次,这就是 import 语句的工作方式;但事实上,这两个字符串有两个不同的用途。

当我们将它传递给 find_module 时,第二次出现用作将在系统上找到的文件名。当我们将字符串传递给 load_module 方法时,第一次出现的字符串被用作已加载模块的系统范围标识符

因此,我们可以为它们使用不同的名称,这意味着我们可以让它像我们复制模块的 python 源文件并加载它一样工作。

socket = imp.load_module('socket_original', *imp.find_module('my_socket_module'))
socket_monkey = imp.load_module('socket_patched',*imp.find_module('my_socket_module'))

def alternative_implementation(blah, blah):
    return 'Happiness'

socket_monkey.original_function = alternative_implementation

import my_sub_module

然后在 my_sub_module 中,我可以导入系统上不存在的“socket_patched”!我们在 my_sub_module.py 中。

import socket_patched
socket_patched.original_function('foo', 'bar')
# This call brings us 'Happiness'

关于python - 如何在运行时复制 python 模块?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11170949/

相关文章:

python - 跳过 numpy.apply_along_axis 内的 RuntimeError

Python C 扩展——维护状态

Python C API : Using PyEval_EvalCode

python - 如果 PyModule_Add* 函数失败,C 扩展是否应该在模块初始化中失败?

python - 段错误 - python C 扩展中的核心转储

python - 反斜杠后的数字值发生变化 os.path.normpath(string)

python - 用于可视化循环 ETA 的进度条

python - 如何通过 DeclarativeMeta 接口(interface)使用反射表?

python - 是否可以预编译(预构建/预安装)Python C/C++ 扩展?

python - 如何在 Tornado HTTP 服务器上通过 HTTP 让长任务成为 "cancellable"?