Consider the following difference between classic classes and the new style classes.
class A():
data = 'abcd'
def __getattr__(self, name):
return getattr(self.data, name)
class B(object):
data = 'abcd'
def __getattr__(self, name):
return getattr(self.data, name)
print(A()[0]) # Prints 'a'
print(B()[0]) # TypeError: 'B' object does not support indexing
I do know that explanation for this property is that new style objects attribute search starts at class instead of instances for builtin operations. But the class object too has __getattr__ defined and why it doesn't get invoked for the missing attribute here which is __getitem__.
I figured out the answer is that the __getattr__ is called only if the attribute search starts at the instance object. But if the attribute search explicitly on the class and instance is skipped __getattr__ is never called.
class B():
data = 'abcd'
def __getattr__(self, name):
print('You are looking for something that doesn\'t exist')
return None
b = B()
b.a
You are looking for something that doesn't exist
B.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class B has no attribute 'a'
Hence in classic classes the search for __getitem__ starts at instance object and __getattr__ is invoked whereas in the new style class the search starts at class object and hence __getattr__ is not invoked.