Skip to content

Instantly share code, notes, and snippets.

@nhumrich
Last active June 13, 2017 17:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nhumrich/ade3d2294fe7b93cf177703c558bf02f to your computer and use it in GitHub Desktop.
Save nhumrich/ade3d2294fe7b93cf177703c558bf02f to your computer and use it in GitHub Desktop.
metaclass wrapping vs __getattr__
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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment