radix asked me if I had a blog post about why changing a class from classic to new-style is a bad idea. After I told him that I didn’t he insisted that I should write one. Since doing so will take less work than finding something else to distract his attention, here it is:
attribute lookup
Attributes on instances of classic classes override attributes of the same name on their class. For example:
>>> class SimpleDescriptor(object): ... def __get__(self, instance, type): ... print 'getting' ... return instance.__dict__['simple'] ... def __set__(self, instance, value): ... print 'setting' ... instance.__dict__['simple'] = value ... >>> class classic: ... simple = SimpleDescriptor() ... >>> x = classic() >>> x.simple = 10 >>> x.simple 10 >>> class newstyle(object): ... simple = SimpleDescriptor() ... >>> x = newstyle() >>> x.simple getting Traceback (most recent call last): File "", line 1, in ? File "", line 4, in __get__ KeyError: 'simple' >>> x.simple = 10 setting >>> x.simple getting 10
As you can see, the descriptor on the new-style class can both handle the setattr and continue to operate after shadowing itself with an instance variable.
Special methods
A particularly interesting consequence of the previous point is that the behavior of special methods changes in some cases:
>>> class x: ... def __init__(self): ... self.__eq__ = lambda other: True ... >>> x() == 10 True >>> class x(object): ... def __init__(self): ... self.__eq__ = lambda other: True ... >>> x() == 10 False >>>
MRO
Rules for determining the method resolution forbid new-style classes in places where classic classes are acceptable. Consider:
>>> class x: pass ... >>> class y(object, x): pass ... >>> class x(object): pass ... >>> class y(object, x): pass ... Traceback (most recent call last): File "", line 1, in ? TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution order (MRO) for bases object, x >>>
Hopefully that’s enough to satisfy radix. Know of other incompatibilities? Please comment.