Set Types ========= .. toctree:: :maxdepth: 1 Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` * :ref:`search` Definition ---------- In Python, a set is a container type that stores an unordered set of zero or more references to :term:`hashable` objects. Values must always be unique within the set, but, need not be the same type. An attempt to add a value that is already present will result in it being discarded (which is useful for removing duplicates from a sequence). Sets support the typical membership test using :keyword:`in`, as well as several additional tests based on set theory like union, intersection, difference and symmetric difference. .. _section_heading-Set: Set --- **MUTABILITY:** Mutable A set is the mutable brother of the :py:class:`frozenset` type. Sets can be constructed in a variety of ways: * Using curly brackets ``{}``. For example: >>> foo = {'a', 'b', 'c', 99, 'b', 'a', 'z'} >>> foo {99, 'a', 'b', 'c', 'z'} .. note:: It is not possible to create an empty set using the curly brackets ``{}`` as that syntax would create a :py:class:`dict`. * Using an expression in curly brackets ``{}`` to create a set comprehension. For example: >>> foo = {x for x in 'foobarbaz' if x in 'fb'} >>> foo {'b', 'f'} * Using the :py:class:`set` constructor, which allows you to do several things: With no arguments, it creates an empty set: >>> foo = set() >>> foo set() When passed something it can iterate over, it builds the set from the items in the sequence: >>> foo = set('foobarbif') >>> foo {'a', 'b', 'f', 'i', 'o', 'r'} When passed a set, this is one way of :term:`shallow-copy` ing the set: >>> foo = set('foobarbif') >>> bar = set(foo) >>> bar {'a', 'b', 'f', 'i', 'o', 'r'} .. _section_heading-Frozen_Set: Frozen Set ---------- **MUTABILITY:** Immutable The immutable brother of the :py:class:`set` type. The advantage to frozensets is that because they are immutable, they can be used as elements in another set, or, as dictionary keys. Frozen sets can only be created using the :py:class:`frozenset` constructor, which operates the same as the :py:class:`set` constructor. Frozensets support all the same common methods as the :py:class:`set` type. .. _section_heading-Set_Operations: Set Operations -------------- All of the set types support a common set of operations which makes working back and forth between them very fluid and easy. The mutable type supports an additional set of operations (no pun intended) that take advantage of the fact that you can modify its contents without creating a new object. .. _section_heading-Common_Set_Operations: Common Set Operations ^^^^^^^^^^^^^^^^^^^^^ Items in a set can be iterated over. However, the order of items returned is arbitrary. For example: >>> foo = {'a', 'b', 'c', 99, 'b', 'a', 'z', 4632} >>> foo {4632, 99, 'a', 'b', 'c', 'z'} Which is a different order than that returned when using a loop: >>> foo = {'a', 'b', 'c', 99, 'b', 'a', 'z', 4632} >>> for item in foo: print(item, end=' ') c 99 a b z 4632 Sets do not support indexing or slicing / striding using the item access operator ``[]``. Attempting to do so will result a :py:exc:`TypeError` being thrown. Sets can be compare to each other. Two sets are considered equal, using the ``==`` operator, if and only if every element of each set is contained in the other: >>> foo = {'a', 'b', 'c'} >>> bar = {'a', 'b', 'c'} >>> bif = {'a', 'b', 'z'} >>> baz = {'a', 'b', 'c', 'd'} >>> foo == bar True >>> foo == bif False >>> foo == baz False Using the other comparison operators, like ``<`` and ``<=``, does not behave the same as in other types. This difference is discussed further in :numref:`table-Common_Set_Operations`, which enumerates the methods common to all set types, mutable and immutable. .. _table-Common_Set_Operations: .. table:: Table of Common Set Operations +------------------------------+--------------------------------------------------------------------------+ | Operation | Description | +==============================+==========================================================================+ | x in s | Membership testing. Returns :py:obj:`True` if x is in s. | | | | | | >>> foo = {'a', 'b', 'c', 'd'} | | | >>> 'a' in foo | | | True | +------------------------------+--------------------------------------------------------------------------+ | x not in s | Membership testing. Returns :py:obj:`True` if x is not in s. | | | | | | >>> foo = {'a', 'b', 'c', 'd'} | | | >>> 5 not in foo | | | True | +------------------------------+--------------------------------------------------------------------------+ | len(s) | Returns the number of items in ``s``. | | | | | | >>> foo = {'a', 'b', 'c', 'd'} | | | >>> len(foo) | | | 4 | +------------------------------+--------------------------------------------------------------------------+ | s.isdisjoint(t) | Returns :py:obj:`True` if ``s`` has no elements in common with ``t``. | | | | | | >>> foo = {'a', 'b', 'c', 'd'} | | | >>> bar = {'a', 'b', 'c', 'd'} | | | >>> bif = {'w', 'x', 'y', 'z'} | | | >>> foo.isdisjoint(bar) | | | False | | | >>> foo.isdisjoint(bif) | | | True | +------------------------------+--------------------------------------------------------------------------+ | s.issubset(t) | Returns :py:obj:`True` if every element in ``s`` is in ``t``. | | | | | s <= t | Use ``<`` to test for proper subset (s additionally != t) | | | | | s < t | >>> foo = {'a', 'b', 'c', 'd'} | | | >>> bar = {'a', 'b', 'c', 'd', 'e', 'f'} | | | >>> bif = {'w', 'x', 'y', 'z'} | | | >>> foo <= bar | | | True | | | >>> boo <= bif | | | False | +------------------------------+--------------------------------------------------------------------------+ | s.issuperset(t) | Returns :py:obj:`True` if every element in ``t`` is in ``s``. | | | | | s >= t | Use ``>`` to test for proper superset (s additional != t) | | | | | s > t | >>> foo = {'a', 'b', 'c', 'd', 'e', 'f'} | | | >>> bar = {'a', 'b', 'c', 'd'} | | | >>> bif = {'w', 'x', 'y', 'z'} | | | >>> foo >= bar | | | True | | | >>> foo >= bif | | | False | +------------------------------+--------------------------------------------------------------------------+ | s.union(\*t) | Return a new set with the element from ``s`` and all others. | | | | | s | t | ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} | | | >>> bar = {15, 20} | | | >>> bif = {300, 999} | | | >>> foo | bar | bif | | | {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 300, 999} | +------------------------------+--------------------------------------------------------------------------+ | s.intersection(\*t) | Return a new set with th elements common to ``s`` and all others. | | | | | s & t & ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> bif = {1, 2, 5, 6, 7, 8, 13, 15 } | | | >>> foo & bar & bif | | | {1, 5, 7} | +------------------------------+--------------------------------------------------------------------------+ | s.difference(\*t) | Return a new set with elements in ``s`` that are not in all others. | | | | | s - t - ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> bif = {1, 2, 5, 6, 7, 8, 13, 15 } | | | >>> foo - bar - bif | | | {4, 10} | +------------------------------+--------------------------------------------------------------------------+ | s.symmetric_difference(t) | Return a new set with elements in ``s`` or ``t`` but not both. | | | | | s ^ t | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> foo ^ bar | | | {2, 4, 6, 8, 10, 11, 13, 15, 17, 19} | +------------------------------+--------------------------------------------------------------------------+ | s.copy() | Return a new set that is a shallow copy of ``s``. | | | | | s ^ t | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} | | | >>> bar = foo.copy() | | | >>> bar | | | {1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10} | +------------------------------+--------------------------------------------------------------------------+ .. note:: In the table above, ``t`` can be any iterable if called on the non-operator versions of :py:meth:`~set.union`, :py:meth:`~set.intersection`, :py:meth:`~set.difference`, :py:meth:`~set.symmetric_difference`, :py:meth:`issubset` and :py:meth:`issuperset`. For example: >>> {1, 2, 3, 4}.union([6, 8, 10, 12]) {1, 2, 3, 4, 6, 8, 10, 12} .. _section_heading-Mutable_Set_Operations: Mutable Set Operations ^^^^^^^^^^^^^^^^^^^^^^ Mutable set types support additional operations that immutable sets do not. These are shown in :numref:`table-Mutable_Set_Operations`. .. _table-Mutable_Set_Operations: .. table:: Table of Mutable Set Operations +------------------------------------+--------------------------------------------------------------------+ | Operation | Description | +====================================+====================================================================+ | s.update(\*t) | Update set ``s`` with elements from all others | | | | | s |= t | ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> bif = {1, 2, 5, 6, 7, 8, 13, 15 } | | | >>> foo |= bar | bif | | | >>> foo | | | {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19} | +------------------------------------+--------------------------------------------------------------------+ | s.intersection_update(\*t) | Update set ``s`` keeping only elements found in it and all others | | | | | s &= t & ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> bif = {1, 2, 5, 6, 7, 8, 13, 15 } | | | >>> foo &= bar & bif | | | >>> foo | | | {1, 5, 7} | +------------------------------------+--------------------------------------------------------------------+ | s.difference_update(\*t) | Update set ``s`` removing elements found in all others | | | | | s -= t - ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> bif = {1, 2, 5, 6, 7, 8, 13, 15 } | | | >>> foo -= bar - bif | | | >>> foo | | | {1, 2, 4, 5, 6, 7, 8, 10} | +------------------------------------+--------------------------------------------------------------------+ | s.symmetric_difference_update(\*t) | Update set ``s`` keeping elements found in either set but not both | | | | | s ^= t ^ ... | >>> foo = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } | | | >>> bar = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19} | | | >>> bif = {1, 2, 5, 6, 7, 8, 13, 15 } | | | >>> foo -= bar - bif | | | >>> foo | | | {1, 2, 4, 5, 6, 7, 8, 10} | +------------------------------------+--------------------------------------------------------------------+ | s.add(x) | Add ``x`` to set ``s`` | | | | | s ^= t ^ ... | >>> foo = {1, 2, 3, 4} | | | >>> foo.add(5) | | | >>> foo | | | {1, 2, 3, 4, 5} | +------------------------------------+--------------------------------------------------------------------+ | s.remove(x) | Remove ``x`` from set ``s``. | | | | | | Raises :py:exc:`KeyError` if x not found. | | | | | s ^= t ^ ... | >>> foo = {1, 2, 3, 4} | | | >>> foo.remove(2) | | | >>> foo | | | {1, 3, 4} | +------------------------------------+--------------------------------------------------------------------+ | s.discard(x) | Remove ``x`` from set ``s``. | | | | | | Won't raise :py:exc:`KeyError` if x not found. | | | | | s ^= t ^ ... | >>> foo = {1, 2, 3, 4} | | | >>> foo.discard(2) | | | >>> foo | | | {1, 3, 4} | +------------------------------------+--------------------------------------------------------------------+ | s.pop() | Remove an arbitrary element from set ``s``. | | | | | | Raises :py:exc:`KeyError` if x not found. | | | | | s ^= t ^ ... | >>> foo = {1, 2, 3, 4} | | | >>> foo.pop() | | | >>> foo | | | 1 | +------------------------------------+--------------------------------------------------------------------+ | s.clear() | Remove all elements from set ``s``. | | | | | s ^= t ^ ... | >>> foo = {1, 2, 3, 4} | | | >>> foo.clear() | | | >>> foo | | | set() | +------------------------------------+--------------------------------------------------------------------+ .. note:: In the table above, ``t`` can be any iterable if called on the non-operator versions of :py:meth:`~set.update`, :py:meth:`~set.intersection_update`, :py:meth:`~set.difference_update` and :py:meth:`~set.symmetric_difference_update`. For example: >>> foo = {1, 2, 3, 4} >>> foo.update([6, 8, 10, 12]) >>> foo {1, 2, 3, 4, 6, 8, 10, 12} .. admonition:: Try it! :class: TryIt Try the following: * Create an empty set. * Create a set from the word "basketball". * Using the set: .. code-block:: python s = {2, 8, 9, 11, 15, 19, 32, 29, 30, 45, 46} * Add the number 32 to the set. * Remove the number 19 from the set. * Find the length of the set. * Determine if 'dogs' are pets given the following set: .. code-block:: python pets = {'cats', 'hamsters', 'snakes', 'dogs', 'rock'}