我可以在Python中替换对象的现有方法吗?

Can I replace an existing method of an object in Python?

本问题已经有最佳答案,请猛点这里访问。

问:在Python(3.6)中,有没有一种方法可以改变现有对象的方法?(我所说的"方法"是指作为参数传递self的函数。)

例子

假设我有一个类Person,它有一些非常有用的方法SayHi()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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美元以上的现金就有好心情。

我可以很容易地创造出一个不在乎金钱、永远快乐的人:

1
2
3
4
5
6
>>> joe.HasGoodMood = lambda: True
>>> joe.SayHi()
Hello!
>>> joe.Cash = 0
>>> joe.SayHi()
Hello!

酷。注意,python如何知道,在使用HasGoodMood的原始实现时,它将无声地传递self作为第一个参数,但如果我将其更改为lambda: True,它将调用不带参数的函数。问题是:如果我想为另一个函数更改默认的HasGoodMood,该函数也接受self作为参数,那该怎么办?

让我们继续我们的例子:如果我想创建一个贪婪的Person,只有当他们拥有超过100美元的资产时,他们才会高兴呢?我想做如下的事情:

1
2
3
>>> greedy_jack = Person()
>>> greedy_jack.HasGoodMood = lambda self: self.Cash > 100
TypeError: <lambda>() missing 1 required positional argument: 'self'

不幸的是,这行不通。有其他方法可以改变方法吗?

免责声明:以上示例仅用于演示目的。我知道我可以使用继承权或保持现金门槛作为Person的财产。但这不是问题的关键。


当你在一个类中编写一个def然后在一个实例上调用它时,这是一个方法,方法调用的机制将在你调用它时填充self参数。

通过在您的实例中分配给HasGoodMood的方法,您不会将新方法放在那里,而是将函数放在属性中。您可以读取属性来获取函数并调用它,虽然这看起来像一个方法调用,但它只是调用一个恰好存储在属性中的函数。您将无法自动获得self参数。

但您已经知道EDOCX1[1]将是什么,因为您要将这个函数分配给一个特定的对象。

1
greedy_jack.HasGoodMood = (lambda self=greedy_jack: self.Cash > 100)

这将函数参数self与变量greedy_jack的当前值相关联。

python中的lambda只能是一行。如果需要更长的函数,可以使用def

1
2
3
4
def greedy_jack_HasGoodMood(self=greedy_jack):
    return self.Cash > 100

greedy_jack.HasGoodMood = greedy_jack_HasGoodMood

要获得更简单的解决方案,请参阅Andrew McDowell的答案。


使用以下提示:

是否可以在不更改同一类的所有其他实例的情况下更改实例的方法实现?

您可以通过使用类型模块为创建的对象分配一个方法,而不影响类来执行以下操作。您需要这样做,因为函数不会自动接收自对象作为第一个变量,但方法会接收自对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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!


继承才是出路。

它可以是简单的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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属性。用上面提到的方式,你可以创造贪婪的人,快乐的人,永远不快乐的人,嬉皮士等。

在我看来,更好的选择是在定义对象时接受cash参数。因此,你可以动态地让人们变得贪婪或正常。(未经测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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