Created
October 12, 2013 00:51
-
-
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/. :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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