I thought I understood the differences between Old-Style Classes™ and New-Style Classes™ in Python. This surprised me, however:
class OldStyle:
def __init__(self):
def override_call():
print "I have overriden %s()" % self
def override_x():
print "I have overriden %s.x()" % self
self.__call__ = override_call
self.x = override_x
def __call__(self):
print "I have not overridden %s()" % self
def x(self):
print "I have not overridden %s.x()" % self
class NewStyle(object):
def __init__(self):
def override_call():
print "I have overriden %s()" % self
def override_x():
print "I have overriden %s.x()" % self
self.__call__ = override_call
self.x = override_x
def __call__(self):
print "I have not overridden %s()" % self
def x(self):
print "I have not overridden %s.x()" % self
>>> o = OldStyle()
>>> o()
I have overriden <__main__.OldStyle instance at 0xb6530>()
>>> o.x()
I have overriden <__main__.OldStyle instance at 0xb6530>.x()
>>> n = NewStyle()
>>> n()
I have not overridden <__main__.NewStyle object at 0x89a10>()
>>> n.x()
I have overriden <__main__.NewStyle object at 0x89a10>.x()
(This is in Python 2.4.1, for reference.)
This seems to apply to any double-underscore (“dunder”) method, not just __call__, but no regular methods. The replacement method is stored in n.__call__, but Python’s usual instance-overrides-class semantics seem to be bypassed for dunder methods, in new-style classes only, with it reading n.__class__.__call__ directly.
There might be a very good reason for this, but it’s not documented in the introduction to new-style classes nor the What’s New for the version of Python they were introduced in. Perhaps the PEP explains why this happens, but I’ll have to re-read it after sleep and caffeination…