File: LP6E/Chapter38/getattr-builtins.py
class GetAttr:
cattr = 88 # Attrs stored on class and instance
def __init__(self): # These skip getattr, but not getattribute
self.iattr = 77
def __len__(self): # Redefine for len(): doesn't run getattr
print('__len__: 66')
return 66
def __getattr__(self, attr): # Provide __str__ if asked, else dummy func
print('getattr:', attr) # Never run for __str__: inherited from object
if attr == '__str__':
return lambda *args: '[Getattr str]'
else:
return lambda *args: None
class GetAttribute:
cattr = 88 # Similar, but catch all attributes
def __init__(self): # Except implicit fetches for built-in ops
self.iattr = 77
def __len__(self): # Redefine for len(): doesn't run getattribute
print('__len__: 66') # But explicit fetches of inherited __str__ do
return 66
def __getattribute__(self, attr):
print('getattribute:', attr)
if attr == '__str__':
return lambda *args: '[GetAttribute str]'
else:
return lambda *args: None
for Class in GetAttr, GetAttribute:
print('\n' + Class.__name__.ljust(50, '='))
X = Class()
# Defined attributes trigger getattribute but not getattr
X.cattr # Class attr (defined – skips getattr)
X.iattr # Instance attr (defined – skips getattr
X.other # Missing attr
len(X) # __len__ defined explicitly: moot
# Built-in ops do not invoke either getattr or getattribute
# No defaults are inherited for these from object superclass
try: X[0] # Tries to invoke __getitem__
except: print('fail []')
try: X + 99 # Ditto, __add__
except: print('fail +')
try: X() # Ditto, __call__
except: print('fail ()')
# But explicit calls invoke both catchers
X.__getitem__(0)
X.__add__(99)
X.__call__()
# The implied object superclass defines a __str__ that precludes getattr
# But the absolute getattribute is not called for implicit fetches either
print(X.__str__()) # __str__: explicit call => only __getattr__ skipped
print(X) # __str__: implicit via built-in => both skipped