| # Copyright 2007 Google, Inc. All Rights Reserved. |
| # Licensed to PSF under a Contributor Agreement. |
| |
| """Abstract Base Classes (ABCs) for collections, according to PEP 3119. |
| |
| DON'T USE THIS MODULE DIRECTLY! The classes here should be imported |
| via collections; they are defined here only to alleviate certain |
| bootstrapping issues. Unit tests are in test_collections. |
| """ |
| |
| from abc import ABCMeta, abstractmethod |
| |
| __all__ = ["Hashable", "Iterable", "Iterator", |
| "Sized", "Container", "Callable", |
| "Set", "MutableSet", |
| "Mapping", "MutableMapping", |
| "MappingView", "KeysView", "ItemsView", "ValuesView", |
| "Sequence", "MutableSequence", |
| "ByteString", |
| ] |
| |
| ### ONE-TRICK PONIES ### |
| |
| class Hashable(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __hash__(self): |
| return 0 |
| |
| @classmethod |
| def __subclasshook__(cls, C): |
| if cls is Hashable: |
| for B in C.__mro__: |
| if "__hash__" in B.__dict__: |
| if B.__dict__["__hash__"]: |
| return True |
| break |
| return NotImplemented |
| |
| |
| class Iterable(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __iter__(self): |
| while False: |
| yield None |
| |
| @classmethod |
| def __subclasshook__(cls, C): |
| if cls is Iterable: |
| if any("__iter__" in B.__dict__ for B in C.__mro__): |
| return True |
| return NotImplemented |
| |
| |
| class Iterator(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __next__(self): |
| raise StopIteration |
| |
| def __iter__(self): |
| return self |
| |
| @classmethod |
| def __subclasshook__(cls, C): |
| if cls is Iterator: |
| if any("__next__" in B.__dict__ for B in C.__mro__): |
| return True |
| return NotImplemented |
| |
| |
| class Sized(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __len__(self): |
| return 0 |
| |
| @classmethod |
| def __subclasshook__(cls, C): |
| if cls is Sized: |
| if any("__len__" in B.__dict__ for B in C.__mro__): |
| return True |
| return NotImplemented |
| |
| |
| class Container(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __contains__(self, x): |
| return False |
| |
| @classmethod |
| def __subclasshook__(cls, C): |
| if cls is Container: |
| if any("__contains__" in B.__dict__ for B in C.__mro__): |
| return True |
| return NotImplemented |
| |
| |
| class Callable(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __contains__(self, x): |
| return False |
| |
| @classmethod |
| def __subclasshook__(cls, C): |
| if cls is Callable: |
| if any("__call__" in B.__dict__ for B in C.__mro__): |
| return True |
| return NotImplemented |
| |
| |
| ### SETS ### |
| |
| |
| class Set(metaclass=ABCMeta): |
| |
| """A set is a finite, iterable container. |
| |
| This class provides concrete generic implementations of all |
| methods except for __contains__, __iter__ and __len__. |
| |
| To override the comparisons (presumably for speed, as the |
| semantics are fixed), all you have to do is redefine __le__ and |
| then the other operations will automatically follow suit. |
| """ |
| |
| @abstractmethod |
| def __contains__(self, value): |
| return False |
| |
| @abstractmethod |
| def __iter__(self): |
| while False: |
| yield None |
| |
| @abstractmethod |
| def __len__(self): |
| return 0 |
| |
| def __le__(self, other): |
| if not isinstance(other, Set): |
| return NotImplemented |
| if len(self) > len(other): |
| return False |
| for elem in self: |
| if elem not in other: |
| return False |
| return True |
| |
| def __lt__(self, other): |
| if not isinstance(other, Set): |
| return NotImplemented |
| return len(self) < len(other) and self.__le__(other) |
| |
| def __eq__(self, other): |
| if not isinstance(other, Set): |
| return NotImplemented |
| return len(self) == len(other) and self.__le__(other) |
| |
| @classmethod |
| def _from_iterable(cls, it): |
| return frozenset(it) |
| |
| def __and__(self, other): |
| if not isinstance(other, Iterable): |
| return NotImplemented |
| return self._from_iterable(value for value in other if value in self) |
| |
| def __or__(self, other): |
| if not isinstance(other, Iterable): |
| return NotImplemented |
| return self._from_iterable(itertools.chain(self, other)) |
| |
| def __sub__(self, other): |
| if not isinstance(other, Set): |
| if not isinstance(other, Iterable): |
| return NotImplemented |
| other = self._from_iterable(other) |
| return self._from_iterable(value for value in self |
| if value not in other) |
| |
| def __xor__(self, other): |
| if not isinstance(other, Set): |
| if not isinstance(other, Iterable): |
| return NotImplemented |
| other = self._from_iterable(other) |
| return (self - other) | (other - self) |
| |
| def _hash(self): |
| """Compute the hash value of a set. |
| |
| Note that we don't define __hash__: not all sets are hashable. |
| But if you define a hashable set type, its __hash__ should |
| call this function. |
| |
| This must be compatible __eq__. |
| |
| All sets ought to compare equal if they contain the same |
| elements, regardless of how they are implemented, and |
| regardless of the order of the elements; so there's not much |
| freedom for __eq__ or __hash__. We match the algorithm used |
| by the built-in frozenset type. |
| """ |
| MAX = sys.maxint |
| MASK = 2 * MAX + 1 |
| n = len(self) |
| h = 1927868237 * (n + 1) |
| h &= MASK |
| for x in self: |
| hx = hash(x) |
| h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167 |
| h &= MASK |
| h = h * 69069 + 907133923 |
| h &= MASK |
| if h > MAX: |
| h -= MASK + 1 |
| if h == -1: |
| h = 590923713 |
| return h |
| |
| Set.register(frozenset) |
| |
| |
| class MutableSet(Set): |
| |
| @abstractmethod |
| def add(self, value): |
| """Return True if it was added, False if already there.""" |
| raise NotImplementedError |
| |
| @abstractmethod |
| def discard(self, value): |
| """Return True if it was deleted, False if not there.""" |
| raise NotImplementedError |
| |
| def pop(self): |
| """Return the popped value. Raise KeyError if empty.""" |
| it = iter(self) |
| try: |
| value = it.__next__() |
| except StopIteration: |
| raise KeyError |
| self.discard(value) |
| return value |
| |
| def toggle(self, value): |
| """Return True if it was added, False if deleted.""" |
| # XXX This implementation is not thread-safe |
| if value in self: |
| self.discard(value) |
| return False |
| else: |
| self.add(value) |
| return True |
| |
| def clear(self): |
| """This is slow (creates N new iterators!) but effective.""" |
| try: |
| while True: |
| self.pop() |
| except KeyError: |
| pass |
| |
| def __ior__(self, it: Iterable): |
| for value in it: |
| self.add(value) |
| return self |
| |
| def __iand__(self, c: Container): |
| for value in self: |
| if value not in c: |
| self.discard(value) |
| return self |
| |
| def __ixor__(self, it: Iterable): |
| # This calls toggle(), so if that is overridded, we call the override |
| for value in it: |
| self.toggle(it) |
| return self |
| |
| def __isub__(self, it: Iterable): |
| for value in it: |
| self.discard(value) |
| return self |
| |
| MutableSet.register(set) |
| |
| |
| ### MAPPINGS ### |
| |
| |
| class Mapping(metaclass=ABCMeta): |
| |
| @abstractmethod |
| def __getitem__(self, key): |
| raise KeyError |
| |
| def get(self, key, default=None): |
| try: |
| return self[key] |
| except KeyError: |
| return default |
| |
| def __contains__(self, key): |
| try: |
| self[key] |
| except KeyError: |
| return False |
| else: |
| return True |
| |
| @abstractmethod |
| def __len__(self): |
| return 0 |
| |
| @abstractmethod |
| def __iter__(self): |
| while False: |
| yield None |
| |
| def keys(self): |
| return KeysView(self) |
| |
| def items(self): |
| return ItemsView(self) |
| |
| def values(self): |
| return ValuesView(self) |
| |
| |
| class MappingView(metaclass=ABCMeta): |
| |
| def __init__(self, mapping): |
| self._mapping = mapping |
| |
| def __len__(self): |
| return len(self._mapping) |
| |
| |
| class KeysView(MappingView, Set): |
| |
| def __contains__(self, key): |
| return key in self._mapping |
| |
| def __iter__(self): |
| for key in self._mapping: |
| yield key |
| |
| KeysView.register(type({}.keys())) |
| |
| |
| class ItemsView(MappingView, Set): |
| |
| def __contains__(self, item): |
| key, value = item |
| try: |
| v = self._mapping[key] |
| except KeyError: |
| return False |
| else: |
| return v == value |
| |
| def __iter__(self): |
| for key in self._mapping: |
| yield (key, self._mapping[key]) |
| |
| ItemsView.register(type({}.items())) |
| |
| |
| class ValuesView(MappingView): |
| |
| def __contains__(self, value): |
| for key in self._mapping: |
| if value == self._mapping[key]: |
| return True |
| return False |
| |
| def __iter__(self): |
| for key in self._mapping: |
| yield self._mapping[key] |
| |
| ValuesView.register(type({}.values())) |
| |
| |
| class MutableMapping(Mapping): |
| |
| @abstractmethod |
| def __setitem__(self, key, value): |
| raise KeyError |
| |
| @abstractmethod |
| def __delitem__(self, key): |
| raise KeyError |
| |
| __marker = object() |
| |
| def pop(self, key, default=__marker): |
| try: |
| value = self[key] |
| except KeyError: |
| if default is self.__marker: |
| raise |
| return default |
| else: |
| del self[key] |
| return value |
| |
| def popitem(self): |
| try: |
| key = next(iter(self)) |
| except StopIteration: |
| raise KeyError |
| value = self[key] |
| del self[key] |
| return key, value |
| |
| def clear(self): |
| try: |
| while True: |
| self.popitem() |
| except KeyError: |
| pass |
| |
| def update(self, other=(), **kwds): |
| if isinstance(other, Mapping): |
| for key in other: |
| self[key] = other[key] |
| elif hasattr(other, "keys"): |
| for key in other.keys(): |
| self[key] = other[key] |
| else: |
| for key, value in other: |
| self[key] = value |
| for key, value in kwds.items(): |
| self[key] = value |
| |
| MutableMapping.register(dict) |
| |
| |
| ### SEQUENCES ### |
| |
| |
| class Sequence(metaclass=ABCMeta): |
| |
| """All the operations on a read-only sequence. |
| |
| Concrete subclasses must override __new__ or __init__, |
| __getitem__, and __len__. |
| """ |
| |
| @abstractmethod |
| def __getitem__(self, index): |
| raise IndexError |
| |
| @abstractmethod |
| def __len__(self): |
| return 0 |
| |
| def __iter__(self): |
| i = 0 |
| while True: |
| try: |
| v = self[i] |
| except IndexError: |
| break |
| yield v |
| i += 1 |
| |
| def __contains__(self, value): |
| for v in self: |
| if v == value: |
| return True |
| return False |
| |
| def __reversed__(self): |
| for i in reversed(range(len(self))): |
| yield self[i] |
| |
| def index(self, value): |
| for i, v in enumerate(self): |
| if v == value: |
| return i |
| raise ValueError |
| |
| def count(self, value): |
| return sum(1 for v in self if v == value) |
| |
| Sequence.register(tuple) |
| Sequence.register(str) |
| |
| |
| class ByteString(Sequence): |
| |
| """This unifies bytes and bytearray. |
| |
| XXX Should add all their methods. |
| """ |
| |
| ByteString.register(bytes) |
| ByteString.register(bytearray) |
| |
| |
| class MutableSequence(Sequence): |
| |
| @abstractmethod |
| def __setitem__(self, index, value): |
| raise IndexError |
| |
| @abstractmethod |
| def __delitem__(self, index): |
| raise IndexError |
| |
| @abstractmethod |
| def insert(self, index, value): |
| raise IndexError |
| |
| def append(self, value): |
| self.insert(len(self), value) |
| |
| def reverse(self): |
| n = len(self) |
| for i in range(n//2): |
| self[i], self[n-i-1] = self[n-i-1], self[i] |
| |
| def extend(self, values): |
| for v in values: |
| self.append(v) |
| |
| def pop(self, index=-1): |
| v = self[index] |
| del self[index] |
| return v |
| |
| def remove(self, value): |
| del self[self.index(value)] |
| |
| def __iadd__(self, values): |
| self.extend(values) |
| |
| MutableSequence.register(list) |
| MutableSequence.register(bytearray) # Multiply inheriting, see ByteString |