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

问题描述

假设我编写了一个元类 M 并且用户编写了我的元类的实例 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'

或者假设我编写了一个A 并且用户编写了我的类的实例 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>",in f
TypeError: <lambda>() missing 1 required positional argument: 'self'
>>> del a.g
>>> a.f()
'foo'

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

解决方法

这里唯一有效的答案与当您询问如何处理反复刺入眼睛时感到的疼痛时医生给出的老栗子回答相同:如果疼痛,请停止这样做。你的眼睛不是为了让手指刺入它而设计的。

所以,是的,当它打破了调用它的现有代码的期望时,将方法添加到实例或类是不好的做法。。 >

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

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

Python 是高度动态的,它为您提供了大量的自由,包括在众所周知的眼睛中戳自己的自由。尽量不要做最后一件事或其他任何可能会损害您的代码的事情,您应该没事。

,

必须使用猴子补丁作为最后手段的极少数情况往往是您无法访问源代码来实现正确的解决方案,或者您不知何故无法定义您希望修复或扩展的类的子类。

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