Last active
January 7, 2019 10:19
-
-
Save L3viathan/fb430eba22bfa38bcbad7508a5b04022 to your computer and use it in GitHub Desktop.
Metaclass to bake hybrid class/instance binding decorators into subclasses
This file contains hidden or 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
| def bind(clsname, first, second=None): | |
| if second is None: # class binding | |
| cls = globals()[clsname] | |
| fn = first | |
| name = fn.fget.__name__ if isinstance(fn, property) else fn.__name__ | |
| setattr(cls, name, fn) | |
| else: # instance binding | |
| self = first | |
| fn = second | |
| name = fn.fget.__name__ if isinstance(fn, property) else fn.__name__ | |
| setattr(self, name, partial(fn, self)) | |
| class BindableMeta(type): | |
| def __new__(cls, name, bases, dct): | |
| def inner(*args): | |
| return bind(name, *args) | |
| dct["bind"] = inner | |
| return type.__new__(cls, name, bases, dct) | |
| class Bindable(metaclass=BindableMeta): | |
| pass | |
| # Examples: | |
| class Foo(Bindable): | |
| pass | |
| @Foo.bind | |
| def bar(self): | |
| return 5 | |
| f = Foo() | |
| assert f.bar() == 5 | |
| @f.bind | |
| def bat(self): | |
| return 5 | |
| assert f.bat() == 5 | |
| g = Foo() | |
| try: | |
| assert g.bat() == 5 | |
| except AttributeError: | |
| pass | |
| else: | |
| raise RuntimeError("shouldn't work") | |
| @Foo.bind | |
| @property | |
| def prop(self): | |
| return 5 | |
| assert f.prop == 5 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment