在实例中覆盖 class 的方法是一种不好的做法吗?

Is it a bad practice to override a method of a class in an instance?

假设我写了一个元class M,用户写了我的元class的一个实例A,它覆盖了一个方法g :

>>> class M(type):
...     def f(self): return self.g()
...     def g(self): return 'foo'
... 
>>> class A(metaclass=M):
...     def g(self): return 'bar'
... 
>>> A.f()  # oops!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
TypeError: g() missing 1 required positional argument: 'self'
>>> del A.g
>>> A.f()
'foo'

或者假设我编写了一个 class A 并且用户编写了我的 class 的一个实例 a,它覆盖了一个方法 g

>>> class A:
...     def f(self): return self.g()
...     def g(self): return 'foo'
... 
>>> a = A()
>>> a.g = lambda self: 'bar'
>>> a.f()  # oops!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
TypeError: <lambda>() missing 1 required positional argument: 'self'
>>> del a.g
>>> a.f()
'foo'

在实例中重写 class 的方法是一种不好的做法吗?

在极少数情况下,必须使用猴子修补作为最后的手段,往往是您无法访问源代码以实施适当的解决方案,或者您无法以某种方式实现定义您希望修复或扩展的 class 的子 class。

在大多数其他情况下,这是一种不好的做法,通常是代码库中的最终手段,当它不尝试修复您没有的第三方解决方案时,它已经背负着太多糟糕的设计决策代码。

这里唯一有效的答案与医生在询问如何处理反复戳自己眼睛时感到的疼痛时给出的老栗子回答相同:如果疼,停止这样做。你的眼睛不是设计来让手指戳进去的。

所以,是的,向实例或 class 添加方法是不好的做法,因为它打破了现有的期望调用它的代码.

请注意,这实际上与您的示例的细节无关。 任何现有代码都可以通过这种方式被破坏,通过用不符合现有预期的实现替换或隐藏其他内容。

您的示例可以轻松修复;要替换 class 上的 M.g 方法,请使用 class 方法。要替换实例上的 A.g,请使用不期望 self 的函数。两者都符合现有代码的预期:调用 g 不需要额外的参数。

Python 是高度动态的,这给了你很多自由,包括在众所周知的眼睛里戳自己的自由。尽量不要做最后一件事或任何其他可能损害您的代码的事情,您应该没事。