(original) (raw)
# 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