🔢 Fundamentals Of Digital Discipleship, Part IX: Sets
Sets & Frozensets
A set object is a mutable, unordered collection of distinct hashable objects with no duplicate entries. Sets do not record element position or order of insertion and since they’re an unordered collection, sets do not support indexing, slicing, or other sequence-like behavior. There’re only two built-in set types, set and frozenset, the latter of which is immutable.
Mutable | list, set, dict, bytearray |
Immutable | int, float, complex, str, bool, bytes, tuple, range, frozenset |
Ordered | list, tuple, str, dict |
Unordered | set, frozenset |
Hashable | str, int, float, complex, bool, bytes, ranges, frozensets, functions, classes, both built-in and user-defined |
Unhashable | list, dict, set, bytearray |
Intersection, union, difference, and symmetric difference, are the main reasons why I would use a set over other container types like a list for instance. If you haven’t gone through our lists & tuples lesson, you may want to complete that understanding first.
Creating Sets
We can create a set, simply, with either the built-in function set()
, or with curly braces {}
much like when we create a dictionary. Since dictionaries can be thought of as a set of key-value pairs, we must be careful to remember that when creating an empty set you have to use set()
, not {}
; the latter creates an empty dictionary. I will demonstrate:
# an empty dictionary
empty_dict = {}
equivalent_to = dict()
# an empty set
empty_set = set()
# set filled with numbers
set_of_ints = {1, 2, 3}
set_of_ints = set([1, 2, 3])
# dictionary of key-value pairs
d = {"one": 1, "two": 2}
# sets have no duplicate entries
# results in {1, 2, 3}
set_of_ints = {1, 1, 2, 2, 3, 3}
Testing For Membership
In very much the same way that we can test for membership in other container types such as lists, tuples, and dictionaries, we can test for membership using the following membership operators.
Membership | |
---|---|
x in s |
Test x for membership in s. |
x not in s |
Test x for non-membership in s. |
# set of literary genres
set_of_genres = {"fantasy", "science fiction", "mystery"}
# result is False
"romance" in set_of_genres
# result is False
"fantasy" not in set_of_genres
# result is True
"fantasy" in set_of_genres
Nothing In Common
Disjoint | |
---|---|
isdisjoint(other) |
Return True if the set has no elements in common with other. Sets are disjoint if and only if their intersection is the empty set. |
s = {1, 2, 3}
other = {4, 5, 6}
empty = set()
# results in True
s.isdisjoint(other)
# results in True
s.isdisjoint(empty)
Subset
We can also test to see if our set appears in its entirety within another set. When we say s < other
it is important to note that this is equivalent to s <= other and s != other
which is to say that it is a ‘proper’ subset.
Subset | |
---|---|
issubset(other) set <= other |
Test whether every element in the set is in other. |
set < other |
Test whether the set is a proper subset of other, that is, set <= other and set != other . |
s = {1, 2, 3}
same = {1, 2, 3}
supers = {0, 1, 2, 3, 4}
# results in True
s.issubset(same)
s <= same
# results in True
s.issubset(supers)
s <= supers
s < supers
# results in False
# s <= same and s != same
s < same
Superset
The superset is the exact opposite to the subset, but otherwise works in the same way inversely.
Superset | |
---|---|
issuperset(other) set >= other |
Test whether every element in other is in the set. |
set > other |
Test whether the set is a proper superset of other, that is, set >= other and set != other. |
s = {0, 1, 2, 3, 4}
same = {0, 1, 2, 3, 4}
subset = {1, 2, 3}
# results in True
s.issuperset(same)
s >= same
# results in True
s.issuperset(subset)
s >= subset
s > subset
# results in False
# s >= same and s != same
s > same
Union
The union of two sets should be reminiscent of concatenation in some ways, while they are different in the way that sets are not appending anything, we are essentially combining two sets.
Union | |
---|---|
union(*others) set | other | ... |
Return a new set with elements from the set and all others. |
s1 = {1, 2}
s2 = {3, 4}
s3 = {5, 6}
# results in {1, 2, 3, 4}
s1.union(s2)
s2.union(s1)
s1 | s2
s2 | s1
# results in {1, 2, 3, 4, 5, 6}
s1.union(s2, s3)
s1 | s2 | s3
⚠️ The following operations are available only to the mutable set and not the immutable frozenset.
Union | |
---|---|
update(*others) set |= other | ... |
Update the set, adding elements from all others. |
s1 = {1, 2}
s2 = {3, 4}
# s1 becomes the union
# s1 is assigned {1, 2, 3, 4}
s1.update(s2)
s1 |= s2
Intersection
The intersection of two sets creates a new set from the elements that intersect, or that they have in common.
Intersection | |
---|---|
intersection(*others) set & other & ... |
Return a new set with elements common to the set and all others. |
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# results in {3, 4}
s1.intersection(s2)
s2.intersection(s1)
s1 & s2
s2 & s1
⚠️ The following operations are available only to the mutable set and not the immutable frozenset.
Intersection | |
---|---|
set &= other & …` | Update the set, keeping only elements found in it and all others. |
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# s1 becomes the intersect
# s1 is assigned {3, 4}
s1.intersection_update(s2)
s1 &= s2
Difference
Whereas the intersection creates a new set from the common elements of two sets, the difference gives us a new set where they do not intersect; however, only the elements in the first set that differ from the elements in the second.
Difference | |
---|---|
difference(*others) set - other - ... |
Return a new set with elements in the set that are not in the others. |
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# results in {1, 2}
s1.difference(s2)
s1 - s2
# results in {5, 6}
s2.difference(s1)
s2 - s1
⚠️ The following operations are available only to the mutable set and not the immutable frozenset.
Difference | |
---|---|
set -= other | ... |
Update the set, removing elements found in others. |
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# s1 is becomes the difference
# s1 is assigned {1, 2}
s1.difference_update(s2)
s1 -= s2
# s2 becomes the difference
# s2 is assigned {5, 6}
s2.difference_update(s1)
s2 -= s1
Symmetric Difference
Whereas difference creates a new set of elements that appear in the first set and are not in the second, the symmetric difference gives us a new set where both differences are used to build a new set.
Symmetric | |
---|---|
set ^ other |
Return a new set with elements in either the set or other but not both. |
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# results in {1, 2, 5, 6}
s1.symmetric_difference(s2)
s2.symmetric_difference(s1)
s1 ^ s2
s2 ^ s1
⚠️ The following operations are available only to the mutable set and not the immutable frozenset.
Symmetric | Result |
---|---|
set ^= other |
Update the set, keeping only elements found in either set, but not in both. |
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# s1 becomes the symmetric difference
# s1 is assigned {1, 2, 5, 6}
s1.symmetric_difference_update(s2)
s1 ^= s2
Mutable Sets Only
Like the update methods, the following operations are available only to the mutable set and not the immutable frozenset. Note that copy() is available to both.
Operation | Result |
---|---|
add(elem) |
Add element elem to the set. |
remove(elem) |
Remove element elem from the set. Raises KeyError if elem is not contained in the set. |
discard(elem) |
Remove element elem from the set if it is present. |
pop() |
Remove and return an arbitrary element from the set. Raises KeyError if the set is empty. |
clear() |
Remove all elements from the set. |
# empty set
s = set()
# add items to set using iteration
# {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for i in range(1, 10+1):
s.add(i)
# remove an element
# {1, 2, 3, 4, 5, 7, 8, 9, 10}
s.remove(6)
# discard an element if present
# without throwing an exception
s.discard(100)
# remove and return arbitrary element
s.pop()
# remove all elements from set
s.clear()
# destroy set object
del s