是否可以恢复(返回到旧方法)python monkey补丁

是否可以恢复(返回到旧方法)python monkey补丁,python,monkeypatching,Python,Monkeypatching,我使用了一个专门的python模块,它在运行时修改了一些Django类方法(也称为猴子补丁)。如果我需要这些“旧”版本,是否可以“返回”到它们来覆盖猴子补丁 比如导入这些类的初始版本 以下是如何在包中进行修补的示例: from django.template.base import FilterExpression def patch_filter_expression(): original_resolve = FilterExpression.resolve def res

我使用了一个专门的python模块,它在运行时修改了一些Django类方法(也称为猴子补丁)。如果我需要这些“旧”版本,是否可以“返回”到它们来覆盖猴子补丁

比如导入这些类的初始版本

以下是如何在包中进行修补的示例:

from django.template.base import FilterExpression

def patch_filter_expression():
    original_resolve = FilterExpression.resolve
    def resolve(self, context, ignore_failures=False):
        return original_resolve(self, context, ignore_failures=False)

    FilterExpression.resolve = resolve

这取决于补丁做了什么。Monkeypatching并没有什么特别之处,它只是将不同的对象分配给一个名称。如果没有其他东西再引用旧值,那么它将从Python的内存中消失

但是,如果修补名称的代码以不同变量的形式保留了对原始对象的引用,则原始对象仍然存在,需要“还原”:

import target.module

_original_function = target.module.target_function

def new_function(*args, **kwargs):
    result = _original_function(*args, **kwargs)
    return result * 5

target.module.target_function = new_function
此处,
target.module
module命名空间中的名称
target\u function
被重新绑定为指向
new\u function
,但原始对象在修补代码的命名空间中仍然作为
\u original\u function
可用

如果这是在函数中完成的,那么原始函数也可以作为闭包使用。对于您的特定示例,您可以通过以下方式获取原始文件:

FilterExpression.resolve.__closure__[0].cell_contents
或者,如果您喜欢按名称访问:

def closure_mapping(func):
    closures, names = func.__closure__, func.__code__.co_freevars
    return {n: c.cell_contents for n, c in zip(names, closures)}

original_resolve = closure_mapping(FilterExpression.resolve)['original_resolve']
否则,您可以告诉Python使用以下命令重新加载原始模块:

这将刷新模块名称空间,将所有全局名称“重置”为导入时设置的名称(保留任何其他名称)


但是,请注意,任何直接引用修补对象(例如类对象)的代码都不会看到更新的对象!这是因为
from target.module import target_函数
在当前命名空间中创建了对
target_函数
对象的新引用,并且不会重新加载原始的
目标。module
模块将更新任何其他直接引用。您必须手动更新这些其他引用,或者重新加载它们的名称空间。

这取决于修补程序所做的操作。Monkeypatching并没有什么特别之处,它只是将不同的对象分配给一个名称。如果没有其他东西再引用旧值,那么它将从Python的内存中消失

但是,如果修补名称的代码以不同变量的形式保留了对原始对象的引用,则原始对象仍然存在,需要“还原”:

import target.module

_original_function = target.module.target_function

def new_function(*args, **kwargs):
    result = _original_function(*args, **kwargs)
    return result * 5

target.module.target_function = new_function
此处,
target.module
module命名空间中的名称
target\u function
被重新绑定为指向
new\u function
,但原始对象在修补代码的命名空间中仍然作为
\u original\u function
可用

如果这是在函数中完成的,那么原始函数也可以作为闭包使用。对于您的特定示例,您可以通过以下方式获取原始文件:

FilterExpression.resolve.__closure__[0].cell_contents
或者,如果您喜欢按名称访问:

def closure_mapping(func):
    closures, names = func.__closure__, func.__code__.co_freevars
    return {n: c.cell_contents for n, c in zip(names, closures)}

original_resolve = closure_mapping(FilterExpression.resolve)['original_resolve']
否则,您可以告诉Python使用以下命令重新加载原始模块:

这将刷新模块名称空间,将所有全局名称“重置”为导入时设置的名称(保留任何其他名称)


但是,请注意,任何直接引用修补对象(例如类对象)的代码都不会看到更新的对象!这是因为
from target.module import target_函数
在当前命名空间中创建了对
target_函数
对象的新引用,并且不会重新加载原始的
目标。module
模块将更新任何其他直接引用。您必须手动更新这些其他引用,或者重新加载它们的名称空间。

这取决于具体情况。这些名字是如何拼凑的?我加了一个例子,视情况而定。名称是如何修补的?我添加了一个示例
importlib。import\u模块
不会创建模块的新副本。它将只检索现有副本,因为有一个。@user2357112:ugh,是的,的确如此。无论如何,
reload()
更容易。
importlib.import\u模块
不会创建模块的新副本。它将只检索现有副本,因为有一个。@user2357112:ugh,是的,的确如此。无论如何,
reload()
更容易。