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
猴子修补实例方法(使用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