blob: ec3e2f838cff876ca7c7021578446bcc43680d4c [file] [log] [blame]
Guido van Rossumcd16bf62007-06-13 18:07:49 +00001# Copyright 2007 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4"""Abstract Base Classes (ABCs) for collections, according to PEP 3119.
5
6DON'T USE THIS MODULE DIRECTLY! The classes here should be imported
Guido van Rossum7eaf8222007-06-18 17:58:50 +00007via collections; they are defined here only to alleviate certain
Guido van Rossumcd16bf62007-06-13 18:07:49 +00008bootstrapping issues. Unit tests are in test_collections.
9"""
10
11from abc import ABCMeta, abstractmethod
12
13__all__ = ["Hashable", "Iterable", "Iterator",
14 "Sized", "Container", "Callable",
15 "Set", "MutableSet",
16 "Mapping", "MutableMapping",
17 "MappingView", "KeysView", "ItemsView", "ValuesView",
18 "Sequence", "MutableSequence",
19 ]
20
21### ONE-TRICK PONIES ###
22
23class Hashable(metaclass=ABCMeta):
24
25 @abstractmethod
26 def __hash__(self):
27 return 0
28
29 @classmethod
30 def __subclasshook__(cls, C):
31 if cls is Hashable:
32 for B in C.__mro__:
33 if "__hash__" in B.__dict__:
34 if B.__dict__["__hash__"]:
35 return True
36 break
37 return NotImplemented
38
39
40class Iterable(metaclass=ABCMeta):
41
42 @abstractmethod
43 def __iter__(self):
44 while False:
45 yield None
46
47 @classmethod
48 def __subclasshook__(cls, C):
49 if cls is Iterable:
50 if any("__iter__" in B.__dict__ for B in C.__mro__):
51 return True
52 return NotImplemented
53
Guido van Rossumcd16bf62007-06-13 18:07:49 +000054
55class Iterator(metaclass=ABCMeta):
56
57 @abstractmethod
58 def __next__(self):
59 raise StopIteration
60
61 def __iter__(self):
62 return self
63
64 @classmethod
65 def __subclasshook__(cls, C):
66 if cls is Iterator:
67 if any("__next__" in B.__dict__ for B in C.__mro__):
68 return True
69 return NotImplemented
70
71
72class Sized(metaclass=ABCMeta):
73
74 @abstractmethod
75 def __len__(self):
76 return 0
77
78 @classmethod
79 def __subclasshook__(cls, C):
80 if cls is Sized:
81 if any("__len__" in B.__dict__ for B in C.__mro__):
82 return True
83 return NotImplemented
84
85
86class Container(metaclass=ABCMeta):
87
88 @abstractmethod
89 def __contains__(self, x):
90 return False
91
92 @classmethod
93 def __subclasshook__(cls, C):
94 if cls is Container:
95 if any("__contains__" in B.__dict__ for B in C.__mro__):
96 return True
97 return NotImplemented
98
99
100class Callable(metaclass=ABCMeta):
101
102 @abstractmethod
103 def __contains__(self, x):
104 return False
105
106 @classmethod
107 def __subclasshook__(cls, C):
108 if cls is Callable:
109 if any("__call__" in B.__dict__ for B in C.__mro__):
110 return True
111 return NotImplemented
112
113
114### SETS ###
115
116
117class Set(metaclass=ABCMeta):
118
119 """A set is a finite, iterable container.
120
121 This class provides concrete generic implementations of all
122 methods except for __contains__, __iter__ and __len__.
123
124 To override the comparisons (presumably for speed, as the
125 semantics are fixed), all you have to do is redefine __le__ and
126 then the other operations will automatically follow suit.
127 """
128
129 @abstractmethod
130 def __contains__(self, value):
131 return False
132
133 @abstractmethod
134 def __iter__(self):
135 while False:
136 yield None
137
138 @abstractmethod
139 def __len__(self):
140 return 0
141
142 def __le__(self, other):
143 if not isinstance(other, Set):
144 return NotImplemented
145 if len(self) > len(other):
146 return False
147 for elem in self:
148 if elem not in other:
149 return False
150 return True
151
152 def __lt__(self, other):
153 if not isinstance(other, Set):
154 return NotImplemented
155 return len(self) < len(other) and self.__le__(other)
156
157 def __eq__(self, other):
158 if not isinstance(other, Set):
159 return NotImplemented
160 return len(self) == len(other) and self.__le__(other)
161
162 @classmethod
163 def _from_iterable(cls, it):
164 return frozenset(it)
165
166 def __and__(self, other):
167 if not isinstance(other, Iterable):
168 return NotImplemented
169 return self._from_iterable(value for value in other if value in self)
170
171 def __or__(self, other):
172 if not isinstance(other, Iterable):
173 return NotImplemented
174 return self._from_iterable(itertools.chain(self, other))
175
176 def __sub__(self, other):
177 if not isinstance(other, Set):
178 if not isinstance(other, Iterable):
179 return NotImplemented
180 other = self._from_iterable(other)
181 return self._from_iterable(value for value in self
182 if value not in other)
183
184 def __xor__(self, other):
185 if not isinstance(other, Set):
186 if not isinstance(other, Iterable):
187 return NotImplemented
188 other = self._from_iterable(other)
189 return (self - other) | (other - self)
190
191 def _hash(self):
192 """Compute the hash value of a set.
193
194 Note that we don't define __hash__: not all sets are hashable.
195 But if you define a hashable set type, its __hash__ should
196 call this function.
197
198 This must be compatible __eq__.
199
200 All sets ought to compare equal if they contain the same
201 elements, regardless of how they are implemented, and
202 regardless of the order of the elements; so there's not much
203 freedom for __eq__ or __hash__. We match the algorithm used
204 by the built-in frozenset type.
205 """
206 MAX = sys.maxint
207 MASK = 2 * MAX + 1
208 n = len(self)
209 h = 1927868237 * (n + 1)
210 h &= MASK
211 for x in self:
212 hx = hash(x)
213 h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167
214 h &= MASK
215 h = h * 69069 + 907133923
216 h &= MASK
217 if h > MAX:
218 h -= MASK + 1
219 if h == -1:
220 h = 590923713
221 return h
222
223Set.register(frozenset)
224
225
226class MutableSet(Set):
227
228 @abstractmethod
229 def add(self, value):
230 """Return True if it was added, False if already there."""
231 raise NotImplementedError
232
233 @abstractmethod
234 def discard(self, value):
235 """Return True if it was deleted, False if not there."""
236 raise NotImplementedError
237
238 def pop(self):
239 """Return the popped value. Raise KeyError if empty."""
240 it = iter(self)
241 try:
242 value = it.__next__()
243 except StopIteration:
244 raise KeyError
245 self.discard(value)
246 return value
247
248 def toggle(self, value):
249 """Return True if it was added, False if deleted."""
250 # XXX This implementation is not thread-safe
251 if value in self:
252 self.discard(value)
253 return False
254 else:
255 self.add(value)
256 return True
257
258 def clear(self):
259 """This is slow (creates N new iterators!) but effective."""
260 try:
261 while True:
262 self.pop()
263 except KeyError:
264 pass
265
266 def __ior__(self, it: Iterable):
267 for value in it:
268 self.add(value)
269 return self
270
271 def __iand__(self, c: Container):
272 for value in self:
273 if value not in c:
274 self.discard(value)
275 return self
276
277 def __ixor__(self, it: Iterable):
278 # This calls toggle(), so if that is overridded, we call the override
279 for value in it:
280 self.toggle(it)
281 return self
282
283 def __isub__(self, it: Iterable):
284 for value in it:
285 self.discard(value)
286 return self
287
288MutableSet.register(set)
289
290
291### MAPPINGS ###
292
293
294class Mapping(metaclass=ABCMeta):
295
296 @abstractmethod
297 def __getitem__(self, key):
298 raise KeyError
299
300 def get(self, key, default=None):
301 try:
302 return self[key]
303 except KeyError:
304 return default
305
306 def __contains__(self, key):
307 try:
308 self[key]
309 except KeyError:
310 return False
311 else:
312 return True
313
314 @abstractmethod
315 def __len__(self):
316 return 0
317
318 @abstractmethod
319 def __iter__(self):
320 while False:
321 yield None
322
323 def keys(self):
324 return KeysView(self)
325
326 def items(self):
327 return ItemsView(self)
328
329 def values(self):
330 return ValuesView(self)
331
332
333class MappingView(metaclass=ABCMeta):
334
335 def __init__(self, mapping):
336 self._mapping = mapping
337
338 def __len__(self):
339 return len(self._mapping)
340
341
342class KeysView(MappingView, Set):
343
344 def __contains__(self, key):
345 return key in self._mapping
346
347 def __iter__(self):
348 for key in self._mapping:
349 yield key
350
351KeysView.register(type({}.keys()))
352
353
354class ItemsView(MappingView, Set):
355
356 def __contains__(self, item):
357 key, value = item
358 try:
359 v = self._mapping[key]
360 except KeyError:
361 return False
362 else:
363 return v == value
364
365 def __iter__(self):
366 for key in self._mapping:
367 yield (key, self._mapping[key])
368
369ItemsView.register(type({}.items()))
370
371
372class ValuesView(MappingView):
373
374 def __contains__(self, value):
375 for key in self._mapping:
376 if value == self._mapping[key]:
377 return True
378 return False
379
380 def __iter__(self):
381 for key in self._mapping:
382 yield self._mapping[key]
383
384ValuesView.register(type({}.values()))
385
386
387class MutableMapping(Mapping):
388
389 @abstractmethod
390 def __setitem__(self, key, value):
391 raise KeyError
392
393 @abstractmethod
394 def __delitem__(self, key):
395 raise KeyError
396
397 __marker = object()
398
399 def pop(self, key, default=__marker):
400 try:
401 value = self[key]
402 except KeyError:
403 if default is self.__marker:
404 raise
405 return default
406 else:
407 del self[key]
408 return value
409
410 def popitem(self):
411 try:
412 key = next(iter(self))
413 except StopIteration:
414 raise KeyError
415 value = self[key]
416 del self[key]
417 return key, value
418
419 def clear(self):
420 try:
421 while True:
422 self.popitem()
423 except KeyError:
424 pass
425
426 def update(self, other=(), **kwds):
427 if isinstance(other, Mapping):
428 for key in other:
429 self[key] = other[key]
430 elif hasattr(other, "keys"):
431 for key in other.keys():
432 self[key] = other[key]
433 else:
434 for key, value in other:
435 self[key] = value
436 for key, value in kwds.items():
437 self[key] = value
438
439MutableMapping.register(dict)
440
441
442### SEQUENCES ###
443
444
445class Sequence(metaclass=ABCMeta):
446
447 """All the operations on a read-only sequence.
448
449 Concrete subclasses must override __new__ or __init__,
450 __getitem__, and __len__.
451 """
452
453 @abstractmethod
454 def __getitem__(self, index):
455 raise IndexError
456
457 @abstractmethod
458 def __len__(self):
459 return 0
460
461 def __iter__(self):
462 i = 0
463 while True:
464 try:
465 v = self[i]
466 except IndexError:
467 break
468 yield v
469 i += 1
470
471 def __contains__(self, value):
472 for v in self:
473 if v == value:
474 return True
475 return False
476
477 def __reversed__(self):
478 for i in reversed(range(len(self))):
479 yield self[i]
480
481 def index(self, value):
482 for i, v in enumerate(self):
483 if v == value:
484 return i
485 raise ValueError
486
487 def count(self, value):
488 return sum(1 for v in self if v == value)
489
490Sequence.register(tuple)
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000491Sequence.register(str)
Guido van Rossum98297ee2007-11-06 21:34:58 +0000492Sequence.register(bytes)
Guido van Rossumbae07c92007-10-08 02:46:15 +0000493Sequence.register(memoryview)
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000494
495
496class MutableSequence(Sequence):
497
498 @abstractmethod
499 def __setitem__(self, index, value):
500 raise IndexError
501
502 @abstractmethod
503 def __delitem__(self, index):
504 raise IndexError
505
506 @abstractmethod
507 def insert(self, index, value):
508 raise IndexError
509
510 def append(self, value):
511 self.insert(len(self), value)
512
513 def reverse(self):
514 n = len(self)
515 for i in range(n//2):
516 self[i], self[n-i-1] = self[n-i-1], self[i]
517
518 def extend(self, values):
519 for v in values:
520 self.append(v)
521
522 def pop(self, index=-1):
523 v = self[index]
524 del self[index]
525 return v
526
527 def remove(self, value):
528 del self[self.index(value)]
529
530 def __iadd__(self, values):
531 self.extend(values)
532
533MutableSequence.register(list)
534MutableSequence.register(bytes)