【问题标题】:Monkey patching in Python using lists在 Python 中使用列表进行猴子修补
【发布时间】:2020-09-27 17:33:25
【问题描述】:

我需要覆盖多个库中的功能,并希望通过迭代我提前指定的列表以编程方式修改函数。

如果我像这样设置我的代码,则修补工作:

def monkey_patch(method):
    def patch(*args, **kwargs):
        print('Patch!')
        return method(*args, **kwargs)
    return patch

def func_a(text):
    print(f'You called func_a with: {text}')
    
func_a('Hello') #  You called func_a with: Hello

original = func_a
func_a = monkey_patch(func_a)
func_a('Hello again') #  Patch!
                      #  You called func_a with: Hello again

func_a = original
func_a('Hello one last time') #  You called func_a with: Hello one last time

但是,尝试由列表驱动它失败了,因为分配认为我正在尝试将函数分配给容器而不是指向的函数:

patch_list = [(func_a, monkey_patch)]
unpatch_list = []

for patch in patch_list:
    original = patch[0]
    patch[0] = patch[1](patch[0]) #  TypeError: 'tuple' object does not support item assignment
    unpatch_list.append((patch[0], original)) #  Store the original function so the patch can be removed later
    
for patch in unpatch_list:
    patch[0] = patch[1] #  TypeError: 'tuple' object does not support item assignment

有没有办法“取消引用”元组中的项目以允许列表方法起作用?

【问题讨论】:

  • 我认为您从根本上误解了赋值的语义。请注意,您所做的并不是真正的猴子补丁。您正在装饰这些功能。要动态地执行此操作,您似乎希望在 inside 实际模块中执行此操作。所以一种方法是直接修改globals()
  • “解除引用”在 Python 中没有意义,Python 没有指针original 指的是您感兴趣的实际函数对象。它已经“取消引用”。请注意,您使用的是tuple 对象,它们是不可变的。但即使您使用列表对象,也不会真正做到您想要做的事情
  • 谢谢@juanpa.arrivillaga! Globals() 正是我所需要的,它现在完美运行。

标签: python monkeypatching


【解决方案1】:

感谢 juanpa.arrivillaga,这是一种使用 globals() 对装饰器和常规猴子补丁执行此操作的方法:

def patch_a(method):
    def patch(*args, **kwargs):
        print('Patch 1!')
        return method(*args, *kwargs)
    return patch

def patch_b(text):
    print(f'Patch 2 called with: {text}')

def func_a(text):
    print(f'You called func_a with: {text}')
    
def func_b(text):
    print(f'You called func_b with: {text}')

patch_list = [
    ('func_a', patch_a, True),
    ('func_b', patch_b, False)
             ]
unpatch_list = []

func_a('Hello')  # You called func_a with: Hello
func_b('Hello again')  # You called func_b with: Hello again

for patch in patch_list:
    unpatch_list.append((patch[0], globals()[patch[0]]))
    
    if patch[2]:
        globals()[patch[0]] = patch[1](globals()[patch[0]])
    else:
        globals()[patch[0]] = patch[1]

func_a('Hello')  # Patch 1!
                 # You called func_a with: Hello
func_b('Hello again')  # Patch 2 called with: Hello again

for patch in unpatch_list:
    globals()[patch[0]] = patch[1]

func_a('Hello')  # You called func_a with: Hello
func_b('Hello again')  # You called func_b with: Hello again

【讨论】:

    猜你喜欢
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-30
    • 2015-03-26
    • 2011-04-25
    相关资源
    最近更新 更多