Skip to content

Instantly share code, notes, and snippets.

@njsmith
Created May 15, 2015 07:45
Show Gist options
  • Select an option

  • Save njsmith/e756e64448e1ec690b7e to your computer and use it in GitHub Desktop.

Select an option

Save njsmith/e756e64448e1ec690b7e to your computer and use it in GitHub Desktop.
Describing the behavior of CPython `list` etc. in pure Python
# 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