def __getattr__(selfattrname)print('traceattrnameself fetches + return getattr(self wrappedattrnamereturn wrapper use enclosing scope name catches all but own attrs delegate to wrapped obj if __name__ ='__main__'@tracer class spamdef display(self)print('spam! @tracer class persondef __init__(selfnamehoursrate)self name name self hours hours self rate rate def pay(self)return self hours self rate spam tracer(spamspam is rebound to wrapper person tracer(personwrapper remembers person accesses outside class traced in-method accesses not traced food spam(food display(print([food fetches]triggers wrapper(triggers __getattr__ bob person('bob' print(bob nameprint(bob pay()bob is really wrapper wrapper embeds person print(''sue person('sue'rate= hours= print(sue nameprint(sue pay()print(bob nameprint(bob pay()print([bob fetchessue fetches]sue is different wrapper with different person bob has different state wrapper attrs not traced it' important to note that this is very different from the tracer decorator we met earlier (despite the name!in "coding function decorators"we looked at decorators that enabled us to trace and time calls to given function or method in contrastby intercepting instance creation callsthe class decorator here allows us to trace an entire object interface--that isaccesses to any of the instance' attributes the following is the output produced by this code under both and ( and later)attribute fetches on instances of both the spam and person classes invoke the __getattr__ logic in the wrapper classbecause food and bob are really instances of wrapperthanks to the decorator' redirection of instance creation callsc:\codepython interfacetracer py tracedisplay coding class decorators