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