class Tracer(object): def __init__(self, obj): self._obj = obj def __getattr__(self, attr): if attr == '_obj': return super().__getattr__(attr) orig = getattr(self._obj, attr) if callable(orig): def wrapped(*args, **kwargs): print("Calling {}.{} with {}, {}".format( repr(self._obj), attr, repr(args), repr(kwargs))) return orig(*args, **kwargs) return wrapped else: print("Getting {}.{} = {}".format(repr(self._obj), attr, repr(orig))) return orig def __setattr__(self, attr, value): if attr == '_obj': super().__setattr__(attr, value) return print("Setting {}.{} to {}".format(repr(self._obj), attr, repr(value))) setattr(self._obj, attr, value)