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



[Home page] Books Code Blog Python Author Train Find ©M.Lutz