File: LP6E/Chapter39/interfacetracer.py

def Tracer(aClass):                                   # On @ decorator
    class Wrapper:
        def __init__(self, *args, **kargs):           # On instance creation
            self.fetches = 0
            self.wrapped = aClass(*args, **kargs)     # Use enclosing-scope name
        def __getattr__(self, attrname):
            print('Trace: ' + attrname)               # Catches all but own attrs
            self.fetches += 1
            return getattr(self.wrapped, attrname)    # Delegate to wrapped obj
    return Wrapper


if __name__ == '__main__':

    @Tracer
    class Hack:                                  # Hack = Tracer(Hack)
        def display(self):                       # Hack is rebound to Wrapper
            print('Hack!' * 3)

    @Tracer
    class Person:                                # Person = Tracer(Person)
        def __init__(self, name, hours, rate):   # Wrapper remembers Person
            self.name = name
            self.hours = hours
            self.rate = rate
        def pay(self):                           # Accesses outside class traced
            return self.hours * self.rate        # In-method accesses not traced

    work = Hack()                                # Triggers Wrapper()
    work.display()                               # Triggers __getattr__
    print([work.fetches])

    print()
    bob = Person('Bob', 40, 50)                  # bob is really a Wrapper
    print(bob.name)                              # Wrapper embeds a Person
    print(bob.pay())

    print()
    sue = Person('Sue', rate=100, hours=60)      # sue is a different Wrapper
    print(sue.name)                              # With a different Person
    print(sue.pay())

    print()
    print(bob.name)                              # bob's state != sue's state
    print(bob.pay())
    print('calls:', [bob.fetches, sue.fetches])  # Wrapper attrs are not traced



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