我可以替换 Python 中对象的现有方法吗? [复制]
Posted
技术标签:
【中文标题】我可以替换 Python 中对象的现有方法吗? [复制]【英文标题】:Can I replace an existing method of an object in Python? [duplicate] 【发布时间】:2019-02-16 22:37:43 【问题描述】:问:有没有办法改变 Python (3.6) 中现有对象的方法? (“方法”是指将self
作为参数传递的函数。)
示例
假设我有一个类Person
有一些非常有用的方法SayHi()
:
class Person(object):
Cash = 100
def HasGoodMood(self):
return self.Cash > 10
def SayHi(self):
if self.HasGoodMood():
print('Hello!')
else:
print('Hmpf.')
>>> joe = Person()
>>> joe.SayHi()
Hello!
如您所见,此人的反应取决于他们当前的情绪,通过方法HasGoodMood()
计算得出。一个默认的人只要身上有超过 10 美元的现金就会心情愉快。
我可以轻松打造一个不在乎钱,时时刻刻快乐的人:
>>> joe.HasGoodMood = lambda: True
>>> joe.SayHi()
Hello!
>>> joe.Cash = 0
>>> joe.SayHi()
Hello!
酷。请注意 Python 是如何知道在使用 HasGoodMood
的原始实现时,它会静默传递 self
作为第一个参数,但如果我将其更改为 lambda: True
,它会调用不带参数的函数。问题是:如果我想将默认的HasGoodMood
更改为另一个也接受self
作为参数的函数怎么办?
让我们继续我们的例子:如果我想创建一个贪婪的Person
,他只有在他们拥有超过 100 美元时才会高兴?我想做类似的事情:
>>> greedy_jack = Person()
>>> greedy_jack.HasGoodMood = lambda self: self.Cash > 100
TypeError: <lambda>() missing 1 required positional argument: 'self'
很遗憾,这不起作用。还有其他方法可以改变方法吗?
免责声明:以上示例仅用于演示目的。我知道我可以使用继承或保留现金门槛作为Person
的属性。但这不是问题的重点。
【问题讨论】:
@Arne 说 dupe 已经过时了,它仅指 Python 2。 @DanielRoseman 在 3 中不再起作用了吗? @Arne 那里有一些与 Python 3 相关的信息,但并不明显。事实上,很难判断这些答案中的大多数是在谈论旧式还是新式类。 据我所知,Arne 的骗局中的前 3 个答案确实适用于 python 3。我认为该骗局目标没有任何问题。 @Aran-Fey 够公平的。 Aaron 的答案是在考虑 Python 3 的情况下编写的。 【参考方案1】:当你在一个类中写一个def
,然后在一个实例上调用它,这就是一个方法,当你调用它时,方法调用的机制会填充self
参数。
通过在您的实例中分配HasGoodMood
,您不是在其中放置新方法,而是将函数放入属性中。您可以读取属性以获取函数并调用它,虽然这看起来像一个方法调用,但它只是调用一个恰好存储在属性中的函数。您不会获得自动提供的 self
参数。
但您已经知道self
将是什么,因为您将此函数分配给一个特定对象。
greedy_jack.HasGoodMood = (lambda self=greedy_jack: self.Cash > 100)
这将函数参数self
与变量greedy_jack
的当前值相关联。
Python 中的 Lambda 只能是一行。如果您需要更长的函数,可以改用def
。
def greedy_jack_HasGoodMood(self=greedy_jack):
return self.Cash > 100
greedy_jack.HasGoodMood = greedy_jack_HasGoodMood
如需更简单的解决方案,请参阅Andrew McDowell's answer。
【讨论】:
如果您能说一下描述符和方法绑定,以解释 OP 的实际问题是什么,那就太好了。但是,虽然我不是您的创可贴解决方案的忠实拥护者,但我认为它不值得投反对票。【参考方案2】:继承才是王道。
它可以很简单:
class Person(object):
Cash = 100
def HasGoodMood(self):
return self.Cash > 10
def SayHi(self):
if self.HasGoodMood():
print('Hello!')
else:
print('Hmpf.')
class newPersonObject(Person):
def HasGoodMood(self):
return self.Cash > 100
>>> greedy = newClassPerson()
>>> greedy.SayHi()
hmpf
当您执行greedy_jack.HasGoodMood = lambda self: self.Cash > 100
时,您在某种程度上是在做同样的事情。您只是覆盖 greedy_jacks
属性。使用上面提到的方式,你可以创造贪婪的人、快乐的人、永远不快乐的人、嬉皮士等。
我认为更好的选择是在定义对象时接受现金参数。因此,您可以动态地使人们变得贪婪或正常。 (未测试)
class Person(object):
def __init__(self, cash_to_be_happy):
self.cash = cash_to_be_happy
def HasGoodMood(self, has_money):
return has_money > self.cash
def SayHi(self, has_money):
if self.HasGoodMood(has_money):
print('Hello!')
else:
print('Hmpf.')
>>> joe = Person(100)
>>> joe.SayHi(150)
Hello!
>>> greedy_joe = Person(200)
>>> greedy_joe.SayHi(150)
Hmpf
【讨论】:
查看免责声明:对于真实场景,这些是正确且绝对可取的解决方案,但是,问题并不是特别要求它们。【参考方案3】:使用以下提示:
Is it possible to change an instance's method implementation without changing all other instances of the same class?
您可以执行以下操作,方法是使用类型模块将方法分配给创建的对象而不影响类。您需要这样做,因为函数不会自动接收 self 对象作为第一个变量,但方法会。
import types
joe = Person()
bob = Person()
joe.SayHi()
>>> Hello!
def greedy_has_good_mood(self):
return self.Cash > 100
joe.HasGoodMood = types.MethodType(greedy_has_good_mood, joe)
joe.SayHi()
>>> Hmpf.
bob.SayHi()
>>> Hello!
【讨论】:
以上是关于我可以替换 Python 中对象的现有方法吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在不子类化的情况下替换 UIWebView 中现有方法的主体