Skip to content

Instantly share code, notes, and snippets.

@demianbrecht
Created October 12, 2013 00:51
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 demianbrecht/6944269 to your computer and use it in GitHub Desktop.
Save demianbrecht/6944269 to your computer and use it in GitHub Desktop.
Some metaclass/abc hackery to implement an ABCMeta-like feature that's tailored more towards duck typing and doesn't use inheritance. Basically, when you want an object to "look" like another, but not "be" one. It's nasty proof of concept and I'd /highly/ suggest not using for /anything/. :)
import abc
def _new(mcls, name, bases, dct):
cls = type.__new__(mcls, name, bases, dct)
abstracts = set()
for k, v in filter(lambda o: not o[0].startswith('__'),
mcls.__looks_like__.__dict__.items()):
if k not in dct:
dct[k] = abc.abstractmethod(lambda: None)
abstracts.add(k)
cls.__abstractmethods__ = abstracts
return cls
def looks_like(cls):
return type('LooksLike{}'.format(cls.__name__), (type,), {
'__new__': _new,
'__looks_like__': cls,
})
def late_bind(obj, name, val):
setattr(obj, name, val)
try:
obj.__abstractmethods__ -= set({name})
except AttributeError:
pass
if __name__ == '__main__':
import unittest
class TestLooksLike(unittest.TestCase):
def test_looks_like(self):
class A:
def foo(self):
return 'foo'
def bar(self):
return 'bar'
# this should fail when instantiated
class B(metaclass=looks_like(A)):
pass
# this should pass as it matches A's interface
class C(metaclass=looks_like(A)):
def foo(self):
return 'foo_'
def bar(self):
return 'bar_'
def baz(self):
return 'baz'
try:
# should result in an error
B()
except TypeError:
pass
C()
# now monkey-patch B with A's interface
late_bind(B, 'foo', lambda: 'foo')
late_bind(B, 'bar', lambda: 'bar')
# and we should be good for instantiation
B()
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment