import functools import inspect from timeit import timeit class Bob: __slots__ = ('a',) def __init__(self): self.a = 5 def say_hello(self): return 'hello' def add(self, b): return self.a + b class MyMeta(type): def __new__(mcls, name, bases, dct): def get_wrapper(meth): def wrapper(self, *args, **kwargs): return self._dispatch_method_call(meth, args, kwargs) return wrapper for attrname in dir(Bob): if attrname.startswith('_') or attrname in dct: continue meth = getattr(Bob, attrname) if not inspect.isfunction(meth): continue wrapper = get_wrapper(meth) wrapper = functools.update_wrapper(wrapper, meth) dct[attrname] = wrapper if '__doc__' not in dct: dct['__doc__'] = Bob.__doc__ return super().__new__(mcls, name, bases, dct) class Alice(metaclass=MyMeta): __slots__ = ('_bob',) def __init__(self, bob): self._bob = bob def _dispatch_method_call(self, meth, args, kwargs): return meth(self._bob, *args, **kwargs) class Alice2: __slots__ = ('_bob',) def __init__(self, bob): self._bob = bob def __getattr__(self, item): return getattr(self._bob, item) def with_alice(): bob = Bob() alice = Alice(bob) alice.say_hello() alice.add(3) def with_alice2(): bob = Bob() alice = Alice2(bob) alice.say_hello() alice.add(3) print('with metaclass:', timeit(with_alice)) print('with __getattr__', timeit(with_alice2))