Multiset
An implementation of a multiset, similar to Python's built-in `set` but allowing elements to occur multiple times. It supports standard set operations like union, intersection, and difference. The library is actively maintained, with version 3.2.0 released in 2024, and incorporates regular updates for new Python versions and minor feature enhancements.
Common errors
-
TypeError: unhashable type: 'Multiset'
cause Attempting to use a mutable `Multiset` object as a key in a dictionary or as an element in a set (or frozenset).fixUse `FrozenMultiset` for scenarios requiring hashability. For example: `my_dict = {FrozenMultiset([1, 2]): 'value'}`. -
AttributeError: 'Multiset' object has no attribute 'keys'
cause Attempting to use a `dict`-specific method (`keys`, `values`, `items`, `popitem`, etc.) directly on a `Multiset` object after the API change in version 2.0.0.fixUse the `multiset` equivalent methods. For `keys`, use `multiset_instance.distinct_elements()`. For `items`, use `multiset_instance.items()`. Refer to the official API documentation for replacements. -
KeyError: 'element_name' (from get, setdefault, remove, or del)
cause The `Multiset.get()` method requires a `default` argument (unlike `dict.get()`). `Multiset.setdefault()` also requires a `default` and will only add an element if the default is positive. Using `multiset_instance.remove(element)` or `del multiset_instance[element]` will raise a `KeyError` if the element is not present, unlike `multiset_instance.discard(element)` which will not.fixFor `get`, always provide a `default` argument, e.g., `my_multiset.get(element, 0)`. For `setdefault`, ensure the `default` is positive if you expect a new element to be added. Use `discard()` if you want to remove an element without raising an error if it's missing.
Warnings
- breaking Python 3.7 is no longer supported starting from `multiset` version 3.1.0. Additionally, support for Python 2.7 and 3.6 was dropped in version 3.0.0.
- breaking In version 2.0.0, the `Multiset` class ceased direct inheritance from `dict`. Consequently, some `dict` methods (e.g., `keys()`, `values()`, `items()`, `fromkeys()`, `get()`, `setdefault()`, `pop()`, `popitem()`) were either removed, renamed, or had their signatures altered.
- gotcha The mutable `Multiset` object is not hashable and therefore cannot be used as a dictionary key or as an element in a `set`. The `FrozenMultiset` class, introduced in version 2.0.0, provides an immutable and hashable alternative.
- gotcha Unlike `collections.Counter`, the `multiset` library automatically removes elements from the multiset whose multiplicity reaches zero. It also strictly only allows positive counts during initialization and other operations.
Install
-
pip install multiset
Imports
- Multiset
from multiset import Multiset
- FrozenMultiset
from multiset import Multiset; my_frozenset = Multiset([...]).freeze() # Old pattern or misunderstanding of API
from multiset import FrozenMultiset
Quickstart
from multiset import Multiset, FrozenMultiset
# Create a mutable multiset from an iterable
m1 = Multiset('banana')
print(f"Initial multiset: {m1}")
# Output: Initial multiset: {b, a, n, a, n, a}
# Add elements
m1.add('apple', multiplicity=2)
print(f"After adding apples: {m1}")
# Output: After adding apples: {b, a, n, a, n, a, apple, apple}
# Check multiplicity (count) of an element
print(f"Count of 'a': {m1.get('a')}")
# Output: Count of 'a': 3
# Perform set operations
m2 = Multiset(['a', 'p', 'p', 'l', 'e'])
intersection = m1 & m2
print(f"Intersection of m1 and m2: {intersection}")
# Output: Intersection of m1 and m2: {a, a, apple, p, l, e}
# Create an immutable, hashable multiset
f1 = FrozenMultiset([1, 1, 2, 3])
f2 = FrozenMultiset([1, 2, 2, 4])
print(f"Frozen multiset f1: {f1}")
# Output: Frozen multiset f1: {1, 1, 2, 3}
# Frozen multisets can be used in sets or as dict keys
my_set_of_multisets = {f1, f2}
print(f"Set of frozen multisets: {my_set_of_multisets}")
# Output: Set of frozen multisets: {{1, 1, 2, 3}, {1, 2, 2, 4}}