Python 使用猴子修补实例方法复制对象

Python copy object with monkey patched instance method

猴子修补实例方法(使用types.MethodType)后,我将猴子修补对象传递给复制它的库(使用copy.copy)并调用猴子修补方法。猴子修补方法被成功调用,但 self 参数引用旧的(未复制的对象)。

import copy
import types


class Person:
    def __init__(self, lang):
        self.lang = lang

    def hello(self, n):
        print(id(self), f"{n} times hello in {self.lang}")


def mean_hello(self, n):
    print(id(self), f"{n} times mean hello in {self.lang}")


a = Person('English')
b = copy.copy(a)
b.lang = "French"
print(id(a), id(b))  # 139885310130440 139885310130720
a.hello(1)  # 139885310130440 1 times hello in English
b.hello(1)  # 139885310130720 1 times hello in French

a.hello = types.MethodType(mean_hello, a)
c = copy.copy(a)
c.lang = 'German'
print(id(a), id(c))  # 139885310130440 139885310130664
a.hello(2)  # 139885310130440 2 times mean hello in English
c.hello(2)  # 139885310130440 2 times mean hello in English

从示例中可以看出,最后一行 c.hello(2) 调用了 monkey patched 方法 self 引用 a。为什么会这样?有没有办法避免这种情况?

正如@MisterMiyagi所指出的,通过types.MethodType创建的方法绑定到对象a(只能在a上调用)并将其赋值给对象c(通过复制)不会使其解除绑定。

@mananony 概述的解决方案是继承 Person 并覆盖子类中的方法。这样,该方法将保持未绑定状态,链接到子类,并且可以用 MeanInner.

类型的任何对象调用

我将子类放在装饰器中,因为在我的真实代码中 Person 实际上代表了很多 类 和 hello 方法,我希望能够更改所有这些.

def mean(person_cls):
    class MeanInner(person_cls):
        def hello(self, n):
            print(id(self), f"{n} times mean hello in {self.lang}")

    return MeanInner


a = mean(Person)('English')
c = copy.copy(a)
c.lang = 'German'
print(id(a), id(c))  # 140407794133424 140407794133536
a.hello(2)  # 140407794133424 2 times mean hello in English
c.hello(2)  # 140407794133536 2 times mean hello in German

而不是使用 copy.copy,您应该使用 classes,因为它们打算使用,并创建继承的 class.

的新实例
class Person:
    def __init__(self, lang):
        self.lang = lang

    def hello(self, n):
        print(id(self), f"{n} times hello in {self.lang}")
        
class MeanInner(Person):
    def hello(self, n):
        print(id(self), f"{n} times mean hello in {self.lang}")


a = MeanInner('English')
c = MeanInner('German')
print(id(a), id(c))  # 139980069269760 139980069269664
a.hello(2)  # 139980069269760 2 times mean hello in English
c.hello(2)  # 139980069269664 2 times mean hello in German