Created
June 10, 2014 21:41
-
-
Save numberoverzero/b88eb9fd0a204be45f4b to your computer and use it in GitHub Desktop.
Chain class with special-casing for __call__, which __getattr__ won't handle (same with __getattribute__)
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
| """ | |
| Why is __call__ a special case? | |
| https://docs.python.org/3.4/reference/datamodel.html#object.__getattribute__ | |
| > Note: This method may still be bypassed when looking up special methods as the result of implicit invocation via language syntax or built-in functions. See Special method lookup. | |
| > In addition to bypassing any instance attributes in the interest of correctness, implicit special method lookup generally also bypasses the __getattribute__() method even of the object’s metaclass | |
| """ | |
| class Chain(object): | |
| def __init__(self, objs): | |
| # Private to avoid clashes when we setattr after compiling | |
| self.__objs = objs | |
| self.__call_partial = None | |
| def __compile(self, method): | |
| _next = noop | |
| for obj in reversed(self.__objs): | |
| func = getattr(obj, method, None) | |
| if func: | |
| _next = functools.partial(func, _next) | |
| return _next | |
| def __getattr__(self, name): | |
| # Bind the compiled partial to self so we avoid | |
| # __getattr__ overhead on the next call | |
| func = self.__compile(name) | |
| setattr(self, name, func) | |
| return func | |
| def __call__(self, *args, **kwargs): | |
| '''special case because __getattr__ does not handle __call__''' | |
| if not self.__call_partial: | |
| self.__call_partial = self.__compile('__call__') | |
| return self.__call_partial(*args, **kwargs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment