Search code examples
pythonoverridinginstancemethod-call

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


Let’s say that I write a metaclass M and a user writes an instance A of my metaclass which overrides a method 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'

Or let’s say that I write a class A and a user writes an instance a of my class which overrides a method 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'

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


Solution

  • The only valid answer here is the same as the old chestnut response the doctor gives when you ask what to do about the pain you feel when you jab yourself in the eye repeatedly: If it hurts, stop doing that. Your eye was not designed to have a finger jabbed in it.

    So, yes, it is bad practice to add a method to an instance or a class when it breaks the expectations of the existing code that calls it.

    Note that this has really nothing to do with the specifics of your examples. Any existing code can be broken this way, by replacing or shadowing something else with an implementation that doesn't fit the existing expectations.

    Your examples can be trivially fixed; to replace the M.g method on a class, use a class method. To replace A.g on the instance, use a function that doesn't expect self. Both would fit the expectations of the existing code: that calling g does not require additional arguments.

    Python is highly dynamic, and that gives you loads and loads of freedom, including the freedom to poke yourself in the proverbial eye. Try not to do that last thing or anything else that could hurt your code, and you should be fine.