Created
May 15, 2015 07:45
-
-
Save njsmith/e756e64448e1ec690b7e to your computer and use it in GitHub Desktop.
Describing the behavior of CPython `list` etc. in pure Python
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
| # Attempt to implement in pure Python a Sequence class which behaves the same | |
| # way as a CPython object that provides tp_as_sequence but not | |
| # tp_as_number (like, for example, the built-in types list, tuple, ...). | |
| def _proper_subinstance(a, b): | |
| ta = type(a) | |
| tb = type(b) | |
| return ta is not tb and issubclass(ta, tb) | |
| class Sequence(object): | |
| ### -- Overload these -- | |
| ### They should NOT return NotImplemented | |
| def _sq_repeat(self, count): | |
| raise NotImplementedError | |
| def _sq_concat(self, other): | |
| raise NotImplementedError | |
| ### -- Leave the rest alone -- | |
| # Rules for 'a * b': | |
| # a is a sequence and b is not: | |
| # call b.__rmul__ | |
| # then fall back on a.__mul__ | |
| # a is not a sequence but b is: | |
| # call a.__mul__ | |
| # then fall back on b.__rmul__ | |
| # a and b are both sequences: | |
| # call a.__mul__ | |
| # The class hierarchy is irrelevant to all of these cases. Of course, | |
| # Python thinks that the class hierarchy is relevant, so we have to pay | |
| # attention to it and counteract its attempts to use its idea of the | |
| # proper dispatch order. | |
| def __do_sq_repeat(self, other): | |
| if hasattr(other, "__index__"): | |
| return self._sq_repeat(other.__index__()) | |
| else: | |
| # Failed, and we *don't* want to unwind and return NotImplemented | |
| raise TypeError("can't multiply sequence by non-int of type %s" | |
| % (type(other).__name__)) | |
| def __mul__(self, other): | |
| if (hasattr(other, "__rmul__") | |
| and not isinstance(other, Sequence) | |
| and not _proper_subinstance(other, self)): | |
| result = other.__rmul__(self): | |
| if result is not NotImplemented: | |
| return result | |
| return self.__do_sq_repeat(other) | |
| def __rmul__(self, other): | |
| if hasattr(other, "__mul__") and _proper_subinstance(self, other): | |
| result = other.__mul__(self) | |
| if result is not NotImplemented: | |
| return result | |
| return self.__do_sq_repeat(other) | |
| # Rules for 'a + b': | |
| # a is a sequence and b is not: | |
| # call b.__radd__ | |
| # then fall back on a.__add__ | |
| # a is not a sequence and b is: | |
| # call a.__add__ | |
| # a and b are both sequences: | |
| # call a.__add__ | |
| # Again, we don't care about the class hierarchy, but Python does. | |
| def __add__(self, other): | |
| if (hasattr(other, "__radd__") | |
| and not isinstance(other, Sequence) | |
| and not _proper_subinstance(other, self)): | |
| result = other.__radd__(self) | |
| if result is not NotImplemented: | |
| return result | |
| return self._sq_concat(result) | |
| # __radd__ intentionally omitted | |
| class list(Sequence): | |
| # ... | |
| pass | |
| class tuple(Sequence): | |
| # ... | |
| pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment