= Python Collections = '''`collections`''' is a module that contains useful container classes. <> ---- == Classes == === ChainMap === To combine mappings in a manner that preserves values hierarchically, consider using a `ChainMap`. The list of mappings is stored internally. Lookups traverse the entire list (in order) until a match is found, while deletions, insertions, and updates only operate on the first mapping. A simple example would be the Python interpreter's own lookup chain: {{{ import builtins from collections import ChainMap pylookup = ChainMap(locals(), globals(), vars(builtins)) }}} To add a new lowest-priority mapping to an existing `ChainMap`, update the `maps` list directly. {{{ from collections import ChainMap inventory = ChainMap(store_inventory, regional_inventory) inventory.maps.append(global_inventory) }}} To add a new highest-priority mapping to an existing `ChainMap`, instead create a new object with the `new_child()` method. {{{ merchandise = inventory.new_child(showroom_inventory) }}} A `ChainMap` is much faster than a series of `update()` calls on a single dictionary, while still offering an ordering of keys that matches such an implementation. In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import ChainMap c: ChainMap[str, int] = ChainMap({"table": 1, "chair": 4}) }}} ---- === Counter === To aggregate a collection of duplicated values into a collection of pairs of unique values and the number of occurences of that value in the original collection, use a `Counter`. {{{ from collections import Counter animals_in_residence = Counter(cats=4, dogs=8) # Counter({'cats': 4, 'dogs': 8}) food_orders = Counter(['eggs', 'ham', 'spam']) # Counter({'eggs': 1, 'ham': 1, 'spam': 1}) favorite_colors = Counter() for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']: favorite_colors[word] += 1 # Counter({'blue': 3, 'red': 2, 'green': 1}) }}} In all cases, the insertion order is preserved. If a lookup fails, a `Counter` returns 0 rather than raising a `KeyError`. To combine `Counter` objects, try: {{{ from collections import Counter c = Counter(x=2, y=2) c.update(Counter(x=1, y=1)) # Counter({'x': 3, 'y': 3}) }}} On the other hand, to subtract one `Counter` from another, try: {{{ from collections import Counter c = Counter(x=2, y=2) c.subtract(Counter(x=1, y=1)) # Counter({'x': 1, 'y': 1}) }}} To delete a pair from a `Counter`, do ''not'' set the corresponding value to `0`. Instead delete the attribute (`del c['key']`). There are also aggregation methods available. `totals()` returns the sum of all values' counts. `most_common(n)` returns the `n` value and count pairs with the highest counts. In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import Counter c: Counter[str] = Counter(['eggs', 'ham', 'spam']) }}} ---- === DefaultDict === A `dict` with a custom factory function for new values. Consider the use case of building a `str`-to-`list` `dict`: {{{ d = dict() for k, v in [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]: d.setdefault(k, []).append(v) }}} The following is equivalent but faster. {{{ from collections import defaultdict d = defaultdict(list) for k, v in [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]: d[k].append(v) }}} In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import defaultdict d: defaultdict[str, list[int]] = defaultdict(list) }}} ---- === Deque === A `deque` is a list with optimized left-hand `pop()`, `append()`, and `extend()` methods. The name is a contraction of 'double-ended queue'. {{{ from collections import deque d = deque(["ham", "spam", "eggs"]) d.append("lastItem") i = d.pop() # "lastItem" d.extend(["lastItem"]) i = d.pop() # "lastItem" d.appendleft("firstItem") i = d.popleft() # "firstItem" d.extendleft(["firstItem"]) i = d.popleft() # "firstItem" }}} To simultaneously `pop()` and `appendleft()` (i.e. move the last item into the first position), try using `d.rotate(1)`. To do the inverse (i.e. move the first item into the last position), try `rotate(-1)`. A `deque` can be cleared with the `clear()` method. To enforce a fixed size on a `deque`, try: {{{ from collections import deque d = deque([1,2,3,4,5], 3) # deque([3, 4, 5]) d.append(6) # deque([4, 5, 6]) d.appendleft(7) # deque([7, 4, 5]) }}} In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import deque d: deque[str] = deque(["ham", "spam", "eggs"]) }}} ---- === NamedTuple === A `tuple` with named indices. {{{ # All equivalent definitions Point = namedtuple('Point', ['x', 'y']) Point = namedtuple('Point', 'x, y') Point = namedtuple('Point', 'x y') # All equivalent instantiations p = Point(11, 22) p = Point(x=11, y=22) p = Point(11, y=22) p = Point(x=11, 22) p = Point._make([11, 22]) p = Point(**{'x': 11, 'y': 22}) }}} The items can be accessed by index (as usual) but also as attributes (i.e. `p.x`). The `getattr` function will also work on a `namedtuple` (i.e. `getattr(p, 'x')`). In the context of [[Python/TypeAnnotation|type annotations]], an additional class is required from `typing`. See [[Python/Typing#NamedTuple|here]] for details. ---- === OrderedDict === A `dict` that preserves insertion order. Per implementation detail, the [[Python/Builtins/Types#Dict|built-in dict]] already preserves order in Python 3.7+. `OrderedDict` is supported as a backwards-compatible and future-proof object. The key differences between `dict` and `OrderedDict` are: * `dict` is optimized for lookups, while `OrderedDict` is optimized for reordering operations * `OrderedDict` equality operations check for order * `OrderedDict` has a different `popitem()` method; `dict` does not have a simple equivalent to `od.popitem(last=False)` * `OrderedDict` has an extra `move_to_end()` method To create a `dict` that preserves last insertion order, try: {{{ class LastUpdatedOrderedDict(OrderedDict): def __setitem__(self, key, value): super().__setitem__(key, value) self.move_to_end(key) }}} In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import OrderedDict od: OrderedDict[str, int] = OrderedDict({"red": 1, "blue": 2) }}} ---- === UserDict === A `dict` that can be subclassed. The [[Python/Builtins/Types#Dict|built-in dict]] can now be subclassed, but `UserDict` is supported as a backwards-compatible object. In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import UserDict ud: UserDict[str, int] = UserDict({"red": 1, "blue": 2}) }}} ---- === UserList === A `list` that can be subclassed. The [[Python/Builtins/Types#List|built-in list]] can now be subclassed, but `UserList` is supported as a backwards-compatible object. In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import UserList ul: UserList[int] = UserList([1, 2]) }}} ---- === UserString === A `str` that can be subclassed. The [[Python/Builtins/Types#Str|built-in str]] can now be subclassed, but `UserString` is supported as a backwards-compatible object. In the context of [[Python/TypeAnnotation|type annotations]], try: {{{ from collections import UserString us: UserString = UserString("foo") }}} ---- == Abstract Base Classes == See [[Python/Collections/Abc|here]] for details. ---- == See also == [[https://docs.python.org/3/library/collections.html|Python collections module documentation]] [[https://pymotw.com/3/collections/|Python Module of the Day article for collections]] ---- CategoryRicottone