Skip to content

Collection Data Types

A collection is a single object that groups multiple values (like basic types) together and so they can be stored in one variable together and worked with as a unit.

Collection Type Example Access values by Use it for
list
["ball", "burmese"]
position #
  • An ordered group of items you can freely add to, remove from, or reorder
  • Not sure? Start here — the default, general-purpose choice
dictionary "dict"
{
  "species": "ball",
  "length_ft": 5
}
Name of a key
  • Values stored under names ("keys") instead of position, like species, length_ft
  • Use it to look values up by name
  • Can't have duplicate keys
tuple
("ball", "burmese")
position #
  • Like a list, but fixed — can't be changed once created
  • Values that should stay exactly as they are, like a coordinate pair
set
{"ball", "burmese"}
Membership (in)
  • An unordered group where duplicates are automatically dropped
  • Use it for fast "is this in here?" checks
Check what type a variable is

type() shows the data type

isinstance() checks whether a value is that type.

weights = [5, 3, 6]

type(weights)        # <class 'list'>

isinstance(5, list)  # True
isinstance(5, dict)  # False

Lists

Create a list

  • A list stores multiple items, in order, inside a single variable. The items can be any type.

    species = ["burmese", "rock", "ball", "blood"]

The index is the numbered position of an item. The index of the first item is 01, next is 1, and so on.

block-beta
block:diagram
    columns 5
    lblValue["Value"] v0["&quot;burmese&quot;"] v1["&quot;rock&quot;"] v2["&quot;ball&quot;"] v3["&quot;blood&quot;"]
    lblIndex["Index"] i0["0"] i1["1"] i2["2"] i3["3"]
    lblNeg["Negative index"] n0["-4"] n1["-3"] n2["-2"] n3["-1"]
end

classDef label fill:none,stroke:none,color:#8A8370
classDef panel fill:#23221E,stroke:#35342E,stroke-width:1px
class i0,i1,i2,i3,n0,n1,n2,n3,lblValue,lblIndex,lblNeg label
class diagram panel

Fig. 5a — list values with their positive and negative indexes

  • The negative index tells you how far from the end it is. It starts counting down from the end instead, starting at -1 for the last item, -2 for the second-to-last, and so on. Each item can be referenced by its positive or negative index.

Access and update items

  • Index with list[index] to return the item at that index (position number) of the list.

    To update the item at that index, set it equal to something else list[index] = new_item.

    Run the below example, and change the indexes to see how they work:

    species = ["burmese", "rock", "ball", "blood"]
    print(species)
    print(species[0])      # "burmese"
    print(species[1])      # "rock"
    print(species[-1])     # "blood"
    
    species[1] = "carpet"  # ["burmese", "carpet", "ball", "blood"]
    print(species)
  • Acccess a range of multiple items at once:

    • Slice with list[start:end] to return a new list containing items from the start index up to (but not including) the end index.

      species[1:3]  # ["rock", "ball"], starts at index 1, stops at (doesn't include) index 3
    • Step with list[start:end:step] to return a new list that can skip items instead of taking every one — start/end are optional so if you leave them off the step is applied to the whole list. A step of -1 walks backward, which is the standard trick for reversing a list. You can also add a start and end range just like a slice.

      species[::2]   # ["burmese", "ball"]  — every 2nd item
      species[::-1]  # ["blood", "ball", "rock", "burmese"]  — reversed
    Assigning to a range

    Setting a slice or step = equal to a list will replaces that whole range at once with the new list.

    A plain slice accepts a replacement of any length — it doesn't need to match the range being replaced.

    species[1:3] = ["carpet", "rock"]  # ["burmese", "carpet", "rock", "blood"]

    A step, on the other hand, requires the replacement to supply exactly as many items as the step selected, or Python raises a ValueError.

    species[::2] = ["carpet", "anaconda"]  # ["carpet", "rock", "anaconda", "blood"]

Loop through a list

  • Lists make it simple to loop directly over the items. The loop runs once for every item in the list, and on each pass the new loop variable, (i.e. specie) is set to the next item in the list.

    for specie in species: 
        print(specie)  # specie is "burmese", then "rock", then "ball", then "blood" — one item per pass
  • If you also want the index of the item alongside the item itself, enumerate() hands back both together.

    for index, specie in enumerate(species): 
        print(index, specie)  # 0 "burmese", then 1 "rock", then 2 "ball", then 3 "blood"

Boolean expressions

  • in, not in checks whether a value exists or is missing in the list.

    "burmese" in species  # True
    
    "anaconda" not in species  # True
  • ==, != checks whether a specific item is equal or not equal to something.

    species[0] == "burmese"  # True
    species[0] != "blood"    # True

    When comparing whole lists, it will say if the two lists have the same contents, in the same order.

    other_species = ["burmese", "rock", "ball", "blood"]
    
    species == other_species        # True
    species != ["ball", "burmese"]  # True
  • is, is not checks whether two variables point to the exact same list object, not just an equal one.

    same_species = species        # another name for the same list
    copy_of_species = species[:]  # a separate list, equal contents
    
    species is same_species       # True, same list
    species is copy_of_species    # False, different list with equal contents
  • is list empty truthiness (boolean value of the whole list)

    • Truthy: a list with contents

    • Falsy: an empty list []

    if species:              # runs if the list has items
        print("found some")
    
    while species:           # loops until the list is empty
        species.pop()

List operations

Inspect

  • len() returns how many items are in a list.

    len(species)  # 4
  • index() finds the position of the first match.

    species.index("burmese")  # 0
  • count() counts how many times a value appears.

    species.count("ball")  # 1

Add item

  • append() adds one item to the end of the list.

    species.append("carpet")  # ["burmese", "rock", "ball", "blood", "carpet"]
  • insert() adds an item at a specific index, without overwriting what's already there.

    species.insert(1, "carpet")  # ["burmese", "carpet", "rock", "ball", "blood"]
  • extend() adds every item from another iterable.

    species.extend(["carpet", "central african rock"])  # ["burmese", "rock", "ball", "blood", "carpet", "central african rock"]

Remove item

  • remove() deletes the first item that matches a given value. If there are duplicate items, it only removes the first one.

    species.remove("rock")  # ["burmese", "ball", "blood"]
  • pop() deletes an item by index and returns it — with no index, it removes the last item.

    species.pop()   # "blood" (removed and returned last item)
    species.pop(0)  # "burmese" (removed and returned item at index 0)
  • del removes an item by index, or can delete the entire list.

    del species[0]  # ["rock", "ball", "blood"]
    del species     # deletes the whole list — species no longer exists
  • clear() empties the list but keeps the (now empty) list around.

    species.clear()  # []

Sort

  • sort() sorts the list in place, alphabetically (or ascending, for numbers) by default. Pass reverse=True to sort in the opposite order, or a key function to control what each item is sorted by.

    species.sort()              # ["ball", "blood", "burmese", "rock"]
    species.sort(reverse=True)  # ["rock", "burmese", "blood", "ball"]
    species.sort(key=len)       # ["rock", "ball", "blood", "burmese"]
  • sorted() does the same job as sort(), but returns a new list, leaving the original untouched.

    sorted(species)  # same as sort() above, but returns a new list
  • reverse() flips the current order in place — different from sort(reverse=True), since it doesn't actually sort, just reverses.

    species.reverse()  # ["blood", "ball", "rock", "burmese"]

Arithmetic

  • min() finds the smallest item.

    min(length_ft)  # 3.5
  • max() finds the largest item.

    max(length_ft)  # 12
  • sum() adds every item together.

    sum(length_ft)  # 25

Create

  • list() builds the same list from any iterable, if you'd rather not use literal brackets.

    list(("burmese", "rock", "ball", "blood"))  # ["burmese", "rock", "ball", "blood"]
  • + joins two lists into a new one.

    species + ["carpet", "boa"]  # ["burmese", "rock", "ball", "blood", "carpet", "boa"]
  • copy() makes a real, independent copy of the list — unlike new_list = old_list, which just points a second name at the same list, so a change through either name shows up in both.

    same_list = species      # same_list and species are the same list — changing one changes both
    backup = species.copy()  # backup is a separate, independent list

List comprehension

  • [expr for item in iterable] builds a new list from an existing iterable in a single line. The expression part can transform each item, not just filter it.

    [s for s in species if len(s) > 4]  # ["burmese", "blood"]
    
    [s.title() for s in species]        # ["Burmese", "Rock", "Ball", "Blood"]

Going further

Practice with lists

Each box below is fully editable — write your answer, then click Run.

1. Access & check membership. Print the second item in the list (index 1), then check whether "rock" is in it.

species = ["indian", "carpet", "rock", "angolan", "blood"]

# your code here

2. Change & add. Change the first item to "burmese", then add "bornean" to the end.

species = ["indian", "carpet", "rock", "angolan", "blood"]

# your code here

print(species)

3. Remove. Remove "carpet" from the list, then pop the last item and print what was removed.

species = ["indian", "carpet", "rock", "angolan", "blood"]

# your code here

4. List comprehension. Build a list of just the names with more than 5 letters.

species = ["indian", "carpet", "rock", "angolan", "blood"]

# your code here

print(long_names)

5. Sort. Sort the list in reverse alphabetical order.

species = ["indian", "carpet", "rock", "angolan", "blood"]

# your code here

print(species)
Show solutions

# 1. Access & check membership
species = ["indian", "carpet", "rock", "angolan", "blood"]
print(species[1])
print("rock" in species)
# 2. Change & add
species = ["indian", "carpet", "rock", "angolan", "blood"]
species[0] = "burmese"
species.append("bornean")
print(species)
# 3. Remove
species = ["indian", "carpet", "rock", "angolan", "blood"]
species.remove("carpet")
last = species.pop()
print(last)
# 4. List comprehension
species = ["indian", "carpet", "rock", "angolan", "blood"]
long_names = [s for s in species if len(s) > 5]
print(long_names)
# 5. Sort
species = ["indian", "carpet", "rock", "angolan", "blood"]
species.sort(reverse=True)
print(species)

Extending lists with collections.deque

A list can already add or remove items from the end cheaply, but doing the same at the frontspecies.insert(0, item) or species.pop(0) — means Python has to shift every other item over. The collections library's deque adds fast appendleft()/popleft() methods for exactly that case. Switch to it when items are being added or removed from both ends often, like a queue of items processed in the order they arrive — not for a list that's mostly read or only changed at the end, where a plain list is simpler and already fast. See the collections library page for the rest of deque's methods (rotate(), maxlen=, and more) and for the other list-adjacent tools it adds.

Dictionaries

Create a dictionary

  • A dictionary stores data as key-value pairs, inside a single variable. Values are looked up by key, not by a numbered position like a list's index — a dict does remember the order keys were added in, but that order isn't how you access anything.

    snake = {
        "species": "ball", 
        "length_ft": 5, 
        "venomous": False
        }
%%{init: {"flowchart": {"nodeSpacing": 15}}}%%
flowchart LR
    subgraph snake["snake"]
        direction LR
        lblKey["`*Key*`"] ~~~ lblVal["`*Value*`"]
        key1["species"] --> val1["'ball'"]
        key2["length_ft"] --> val2["5"]
        key3["venomous"] --> val3["False"]
    end

    style key1 stroke:#3f6b52,stroke-width:2px
    style key2 stroke:#3f6b52,stroke-width:2px
    style key3 stroke:#3f6b52,stroke-width:2px
    style lblKey fill:none,stroke:none,color:#8A8370
    style lblVal fill:none,stroke:none,color:#8A8370

Fig. 5b — a dict's key-value pairs

- Each **key** points to exactly one value.

    - **A key's type** can be a string, int, float, or tuple

    - **No duplicate keys** — assigning a value to an existing key overwrites its value.

- A **value** can be any type.

Access a value

  • dict[key] accesses a value by key, in square brackets. This will raise an error if the key is not present, so best practice is to use the below get() instead.

    snake["species"]  # "ball"
  • get() does the same thing, but returns None if the key is not in the dict. You can provide an optinal default value to fall back on that will be returned if they key is not in the dict.

    snake.get("species")        # "ball"
    snake.get("weight_lbs", 0)  # 0 — key is missing, so the default is returned instead of None

Loop through a dictionary

  • Looping directly over a dictionary gives you its keys, one at a time — the loop runs once for every key in the dictionary, and on each pass the loop variable, (i.e. key) is set to the next key.

    for key in snake: 
        print(key)  # species  length_ft  venomous
  • Loop over .values() to get just the values instead.

    for value in snake.values(): 
        print(value)              # ball  5  False
  • Loop over .items() to get both the key and the value together.

    for key, value in snake.items(): 
        print(key, value)             # species ball  length_ft 5  venomous False

Boolean expressions

  • in checks whether a key exists at all.

    "species" in snake  # True
  • not in checks whether a key is missing. Or compare a specific value directly, like snake["length_ft"] > 2.

    "habitat" not in snake  # True
  • == checks whether two dictionaries have the same keys and values, even if they're different objects. != checks whether they differ.

    other_snake = {"species": "ball", "length_ft": 5, "venomous": False}
    
    snake == other_snake  # True
  • is checks whether two variables point to the exact same dictionary object, not just an equal one — use == to compare contents.

    same_snake = snake        # another name for the same dictionary
    copy_of_snake = snake.copy()  # a separate dictionary, equal contents
    
    snake is same_snake       # True, same dictionary
    snake is copy_of_snake    # False, different dictionary with equal contents
  • boolean expression:

    • Truthy: a dictionary with at least one key

    • Falsy: an empty dictionary {}

    if snake:                    # runs if dictionary is not empty
        print("found a record")
    
    while snake:                 # loops until the dictionary is empty
        snake.popitem()

Dictionary operations

Inspect

  • len() returns how many key-value pairs are in a dictionary.

    len(snake)  # 3

Update

  • dict[key] = value sets a key's value — changes it if the key already exists, adds it if not.

    snake["length_ft"] = 6           # {'species': 'ball', 'length_ft': 6, 'venomous': False}
    snake["origin"] = "west africa"  # {'species': 'ball', 'length_ft': 5, 'venomous': False, 'origin': 'west africa'}
  • update() does the same for multiple keys at once — changes any that already exist, and adds any that don't.

    snake.update({"venomous": False, "docile": True})  # {'species': 'ball', 'length_ft': 5, 'venomous': False, 'docile': True}

Remove

  • pop() removes a key and returns its value.

    snake.pop("venomous")  # False (removed and returned)
  • popitem() removes and returns the last inserted key-value pair, as a tuple.

    snake.popitem()  # ('venomous', False) — removes the last inserted pair
  • del removes a key-value pair by key.

    del snake["species"]  # {'length_ft': 5, 'venomous': False}
  • clear() empties the dictionary but keeps the (now empty) dictionary around.

    snake.clear()  # {}

Create

  • dict() builds the same dictionary using keyword arguments, if you'd rather not use literal braces.

    dict(species="ball", length_ft=5, venomous=False)  # {'species': 'ball', 'length_ft': 5, 'venomous': False}
  • copy() makes a real, independent copy of the dictionary — unlike new_dict = old_dict, which just points a second name at the same dictionary, so a change to either would effect both.

    same_dict = snake      # same_dict and snake are the same dictionary — mutating one mutates both
    backup = snake.copy()  # backup is a separate, independent dictionary
Nested dictionaries

A dictionary's values can be other dictionaries. Useful for grouping related records under one variable, like a whole collection of snakes keyed by species.

Chain operations one after the other to reach a value nested inside an inner dictionary.

snakes = {
    "ball": snake, 
    "burmese": {
        "length_ft": 16, 
        "venomous": False
        }
    }
snakes["burmese"]["length_ft"]  # 16

Going further

Practice with dictionaries

Each box below is fully editable — write your answer, then click Run.

1. Add & change. Add a "docile" key set to True, then change "length_ft" to 16.

snake = {"species": "burmese", "length_ft": 12, "venomous": False}

# your code here

print(snake)

2. Remove. Remove the "venomous" key from the dictionary.

snake = {"species": "burmese", "length_ft": 16, "venomous": False}

# your code here

print(snake)

3. Loop and collect. Build a list of just the dictionary's values, using a loop (not list(snake.values())).

snake = {"species": "burmese", "length_ft": 16, "venomous": False}
values = []

# your code here

print(values)

4. Nested access. Given the dictionary below, print the ball python's length.

snakes = {
    "burmese": {"length_ft": 16, "venomous": False},
    "ball": {"length_ft": 5, "venomous": False},
}

# your code here
Show solutions

# 1. Add & change
snake = {"species": "burmese", "length_ft": 12, "venomous": False}
snake["docile"] = True
snake["length_ft"] = 16
print(snake)
# 2. Remove
snake = {"species": "burmese", "length_ft": 16, "venomous": False}
del snake["venomous"]
print(snake)
# 3. Loop and collect
snake = {"species": "burmese", "length_ft": 16, "venomous": False}
values = []
for value in snake.values():
    values.append(value)
print(values)
# 4. Nested access
snakes = {
    "burmese": {"length_ft": 16, "venomous": False},
    "ball": {"length_ft": 5, "venomous": False},
}
print(snakes["ball"]["length_ft"])

Extending dicts with collections

A plain dict can tally counts or group items, but both take extra setup code: checking whether a key exists before incrementing it, or before appending to a list under it. The collections library adds several dicts that handle cases like these automatically.

  • Counter counts items in a sequence directly — reach for it as soon as a dict's job is "how many times does each item show up."
  • defaultdict supplies an empty value (a list, a set, 0) the first time a new key is used, so grouping items under keys that aren't known ahead of time doesn't need an if key not in dict check before every write.
  • OrderedDict is worth reaching for only when order itself needs to be compared or reordered — a plain dict already remembers insertion order, but its == ignores that order, and it has no move_to_end().
  • ChainMap layers several dicts together — like a set of overrides checked before a set of defaults — without copying or merging them into a new dict.

See the collections library page for the full method list on each of these.

Tuples

A tuple stores multiple items, in order, written in parentheses. They are immutable so the items can't be changed once its created.

species = ("burmese", "rock", "ball", "blood")
block-beta
  block:diagram
    columns 5
    lblValue["Value"] v0["&quot;burmese&quot;"] v1["&quot;rock&quot;"] v2["&quot;ball&quot;"] v3["&quot;blood&quot;"]
    lblIndex["Index"] i0["0"] i1["1"] i2["2"] i3["3"]
    lblNeg["Negative index"] n0["-4"] n1["-3"] n2["-2"] n3["-1"]
  end

  classDef label fill:none,stroke:none,color:#8A8370
  classDef panel fill:#23221E,stroke:#35342E,stroke-width:1px
  class i0,i1,i2,i3,n0,n1,n2,n3,lblValue,lblIndex,lblNeg label
  class diagram panel

Fig. 5c — tuple values with their positive and negative indexes

The index of the first item is 01, next is 1, and so on.

The negative index starts counting down from the end instead, starting at -1 for the last item, -2 for the second-to-last, and so on. Each item can be referenced by its positive or negative index.

Access items

  • Index with tuple[index].

    species[0]   # "burmese"
    species[-1]  # "blood"
  • A slice tuple[start:end] returns a new tuple containing items from start index up to (but not including) the end index.

    species[1:3]  # ("rock", "ball")

Loop through a tuple

  • The loop runs once for every item in the tuple, and on each pass the loop variable, (i.e. specie) is set to the next item in the tuple.

    for specie in species: 
        print(specie)       # burmese  rock  ball  blood
  • If you also want the index alongside the item, enumerate() hands back both together — works the same as on a list, since tuples support indexing too.

    for index, specie in enumerate(species): 
        print(index, specie)  # 0 "burmese", then 1 "rock", then 2 "ball", then 3 "blood"

Boolean expressions

  • in checks whether a value exists in the tuple.

    "rock" in species  # True
  • not in checks whether a value is missing from the tuple.

    "carpet" not in species  # True
  • == checks whether two tuples have the same contents, in the same order. != checks whether they differ. Or compare a specific item directly, like species[0] == "burmese".

    other_species = ("burmese", "rock", "ball", "blood")
    
    species == other_species  # True
    species != ("burmese",)   # True
  • is checks whether two variables point to the exact same tuple object, not just an equal one — use == to compare contents.

    same_species = species  # another name for the same tuple
    
    species is same_species          # True, same tuple
    species is ("burmese", "rock", "ball", "blood")  # False, different tuple with equal contents
  • boolean expression:

    • Truthy: a tuple with contents

    • Falsy: an empty tuple ()

    if species:              # runs — species tuple has items
        print("found some")

Packing and unpacking

  • Packing: writing several values separated by commas, with or without the surrounding parentheses, implicitly builds a tuple.

    species = "burmese", "rock", "ball", "blood"  # parentheses optional — still a tuple
    type(species)                                 # <class 'tuple'>
  • Unpacking: assigns each item in a tuple to its own variable in one line. The number of variables has to match the number of items.

    a, b, c, d = species  # a="burmese"  b="rock"  c="ball"  d="blood"

    A match statement can do this same unpacking while also branching on the tuple's shape or specific values.

    snake = (12, "ball")
    match snake:
        case (length, "ball"):                      # tuple with 2 items, where second is "ball"
            print(f"a {length} ft ball python")     # in this example, this case will run
        case (length, specie):                      # tuple with any 2 items
            print(f"a {length} ft {specie} python")
        case (length,):                             # tuple with any 1 item
            print(f"just a length: {length}")
        case _:                                     # 0 items, or tuple with more than 2 items
            print("invalid format")

Tuple operations

Inspect

  • len() returns how many items are in a tuple.

    len(species)  # 4
  • count() counts how many times a value appears.

    species.count("burmese")  # 1
  • index() finds the position of the first match.

    species.index("ball")  # 2

Arithmetic

  • min() finds the smallest item.

    length_ft = (12, 4.5, 3.5, 5)
    min(length_ft)                 # 3.5
  • max() finds the largest item.

    max(length_ft)  # 12
  • sum() adds every item together.

    sum(length_ft)  # 25

Convert to modify

  • Convert to list -> edit -> convert back to tuple builds a new tuple since a tuple can't be edited directly.

    species_list = list(species)   # convert tuple to a list
    species_list.append("carpet")  # edit it like any list
    species = tuple(species_list)  # convert back to tuple and reassign

Create

  • tuple() builds the same tuple from any iterable, if you'd rather not use literal parentheses.

    tuple(["burmese", "rock", "ball", "blood"])  # ("burmese", "rock", "ball", "blood")

Going further

Practice with tuples

Each box below is fully editable — write your answer, then click Run.

1. Access & check membership. Print the last item, then check whether "carpet" is in the tuple.

species = ("indian", "carpet", "rock", "blood")

# your code here

2. Unpacking. Unpack the tuple into four variables named a, b, c, d, then print them.

species = ("indian", "carpet", "rock", "blood")

# your code here

3. Work around immutability. Tuples can't be appended to directly — build a new tuple with "angolan" added to the end, using +.

species = ("indian", "carpet", "rock", "blood")

# your code here

print(species)
Show solutions

# 1. Access & check membership
species = ("indian", "carpet", "rock", "blood")
print(species[-1])
print("carpet" in species)
# 2. Unpacking
species = ("indian", "carpet", "rock", "blood")
a, b, c, d = species
print(a, b, c, d)
# 3. Work around immutability
species = ("indian", "carpet", "rock", "blood")
species = species + ("angolan",)
print(species)

Extending tuples with collections.namedtuple

A plain tuple's items can only be accessed by position — snake[1] doesn't say what that value actually means without checking back how the tuple was built. The collections library's namedtuple builds a tuple type with named fields, so the same value reads as snake.length_ft. Switch to it once a tuple's positions start needing a mental lookup table to remember, or once several tuples share the same shape throughout a program — a single namedtuple definition documents that shape once instead of repeating a comment at every literal. See the collections library page for namedtuple's other methods (_asdict(), _replace(), default field values) and the rest of the module.

Sets

A set stores multiple items, in no particular order, inside a single variable — written with curly braces.

Because items have no fixed position, there's no indexing. Duplicates are irrelevant because adding a value that's already there changes nothing; a set can only ever hold each value once.

species = {"burmese", "rock", "ball", "blood"}  # order not fixed
block-beta
  block:diagram
    columns 5
    lblValue["Unordered set: "] v0["&quot;burmese&quot;"] v1["&quot;rock&quot;"] v2["&quot;ball&quot;"] v3["&quot;blood&quot;"]
  end

  classDef label fill:none,stroke:none,color:#8A8370
  classDef panel fill:#23221E,stroke:#35342E,stroke-width:1px
  class lblValue label
  class diagram panel

Fig. 5d — set values with no fixed order

Loop through a set

The loop runs once for every item in the set, in no guaranteed order, and on each pass the loop variable, (i.e. specie) is set to the next item.

for specie in species: 
    print(specie)       # burmese  rock  ball  blood — order not guaranteed

Boolean expressions

  • in checks whether a value exists — and does it far faster than a list or tuple, no matter how large the set gets, since Python looks it up directly instead of scanning item by item.

    "burmese" in species  # True
  • not in checks whether a value is missing from the set.

    "cobra" not in species  # True
  • == checks whether two sets have the same contents, regardless of order. != checks whether they differ.

    other_species = {"burmese", "rock", "ball", "blood"}
    
    species == other_species  # True
  • is checks whether two variables point to the exact same set object, not just an equal one — use == to compare contents.

    same_species = species        # another name for the same set
    copy_of_species = species.copy()  # a separate set, equal contents
    
    species is same_species       # True, same set
    species is copy_of_species    # False, different set with equal contents
  • issubset(), issuperset(), isdisjoint() compare two sets and hand back a bool too — see Set operations: Compare for the full rundown.

  • boolean expression:

    • Truthy: a set with contents

    • Falsy: an empty set — written set(), not {}, since {} creates an empty dict instead

    if species:              # runs — the set has items
        print("found some")
    
    while species:           # loops until the set is empty
        species.pop()

Set operations

Inspect

  • len() returns how many items are in a set.

    len(species)  # 4

Arithmetic

  • min() finds the smallest item.

    length_ft = {12, 4.5, 3.5, 5}
    min(length_ft)                 # 3.5
  • max() finds the largest item.

    max(length_ft)  # 12
  • sum() adds every item together.

    sum(length_ft)  # 25

Update

  • add() adds a single item. Adding a value that's already present changes nothing.

    species.add("carpet")   # {"burmese", "rock", "ball", "blood", "carpet"}
    species.add("burmese")  # already there — no change
  • update() adds every item from another iterable, one at a time.

    species.update(["carpet", "boa"])  # adds "boa"; "carpet" was already there

Remove

  • remove() deletes an item, raising an error if it isn't there.

    species.remove("rock")  # errors if "rock" isn't in the set
  • discard() does the same but stays silent if the item's missing.

    species.discard("rock")  # no error either way
  • pop() removes and returns an arbitrary item, since there's no "last" item in an unordered collection.

    species.pop()  # removes and returns *some* item — which one isn't guaranteed
  • clear() empties the set.

    species.clear()  # set()

Combine

Sets support the same operations as sets in math class — useful for comparing two groups directly instead of writing your own loop to do it.

constrictors = {"ball", "burmese", "boa"}
pet_friendly = {"ball", "burmese", "corn snake"}
  • | union — everything in either set.

    constrictors | pet_friendly  # {"ball", "burmese", "boa", "corn snake"}
  • & intersection — only what's in both.

    constrictors & pet_friendly  # {"ball", "burmese"}
  • - difference — in the first set, but not the second.

    constrictors - pet_friendly  # {"boa"}
  • ^ symmetric difference — in one set or the other, but not both.

    constrictors ^ pet_friendly  # {"boa", "corn snake"}

Compare

These check a relationship between two sets and hand back a bool, rather than building a new set the way Combine does.

  • issubset() checks whether every item in this set is also in another set.

    {"ball", "burmese"}.issubset(constrictors)  # True
  • issuperset() checks whether this set contains every item in another set — the reverse of issubset().

    constrictors.issuperset({"ball"})  # True
  • isdisjoint() checks whether two sets have no items in common.

    constrictors.isdisjoint({"cobra", "viper"})  # True

Create

  • set() builds the same set from any iterable, if you'd rather not use literal braces.

    set(["burmese", "rock", "ball", "blood"])  # {'burmese', 'rock', 'ball', 'blood'}
  • copy() makes a real, independent copy of the set — unlike new_set = old_set, which just points a second name at the same set, so a change through either name shows up in both.

    same_set = species       # same_set and species are the same set — mutating one mutates both
    backup = species.copy()  # backup is a separate, independent set
Removing duplicates from a list

Converting a list to a set and back is a common one-line way to drop duplicates — though it also throws away the original order, unless you sort or otherwise re-derive it.

species = ["ball", "burmese", "ball", "boa", "burmese"]
list(set(species))  # ["burmese", "ball", "boa"] — order not guaranteed

Going further

Practice with sets

Each box below is fully editable — write your answer, then click Run.

1. Check membership & add. Check whether "carpet" is in the set, then add it.

species = {"indian", "rock", "blood", "angolan"}

# your code here

2. Remove. Discard "rock" from the set — using the method that won't error even if it's already gone.

species = {"indian", "rock", "blood", "angolan"}

# your code here

print(species)

3. Deduplicate. Given the list below (with repeats), build a set from it to remove duplicates, then convert it back to a list.

names = ["ball", "burmese", "ball", "boa", "burmese"]

# your code here

print(unique_names)
Show solutions

# 1. Check membership & add
species = {"indian", "rock", "blood", "angolan"}
print("carpet" in species)
species.add("carpet")
# 2. Remove
species = {"indian", "rock", "blood", "angolan"}
species.discard("rock")
print(species)
# 3. Deduplicate
names = ["ball", "burmese", "ball", "boa", "burmese"]
unique_names = list(set(names))
print(unique_names)


  1. In programming, counting generally starts at 0, not 1. That's because an index isn't really a count of "how manyth" item something is — it's an offset, the number of steps from the start. The first item is 0 steps away, so it gets index 0. It feels different from counting out loud ("first, second, third..."), but it's the convention nearly every programming language follows.