With languages like C++ and Java, containers are add-on libraries and not integral to the language. In Python, the essential nature of containers for programming is acknowledged by building them into the core of the language: both lists and associative arrays (a.k.a. maps, dictionaries, hash tables) are fundamental data types.
In addition, the for statement automatically
iterates through lists rather than just counting through a sequence
of numbers. Python formalizes this by automatically making
for use an iterator that works through a sequence.
Here’s an example:
# list.py
list = [ 1, 3, 5, 7, 9, 11 ]
print(list)
list.append(13)
for x in list:
print(x)The first line creates a list. You can add new elements to lists
(here, append() is used) and they will automatically
resize themselves. The for statement creates an
iterator x which takes on each value in the list.
Notice that there aren’t any type declarations: the object names simply appear, and Python infers their type by the way that you use them. You’ll find after you’ve worked with Python for a short while that you’ve been using up a lot of brain cycles parsing semicolons, curly braces, and all sorts of other extra verbiage that was demanded by your non-Python programming language but didn’t actually describe what your program was supposed to do.
A list holds an ordered, mutable sequence of any objects.
Indexing starts at zero, and negative indices count from the end. A
slice [start:stop:step] copies a subrange,
with stop excluded:
# slicing.py
xs = [10, 20, 30, 40, 50]
print(xs[0], xs[-1]) # 10 50: first and last
print(xs[1:3]) # [20, 30]: the stop index is excluded
print(xs[:2]) # [10, 20]: from the start
print(xs[2:]) # [30, 40, 50]: to the end
print(xs[::2]) # [10, 30, 50]: every second item
print(xs[::-1]) # [50, 40, 30, 20, 10]: reversed
xs.append(60)
xs.insert(0, 5)
print(len(xs), 30 in xs) # 7 TrueSlicing works on any sequence, including strings and tuples.
A tuple is an immutable sequence. The comma makes the tuple, not the parentheses. Tuples are the natural way to return several values from a function and to group values for unpacking:
# tuples.py
point = (3, 4)
x, y = point # unpacking
print(x, y) # 3 4
single = (42,) # a one-element tuple needs the trailing comma
print(len(single)) # 1
def min_max(values):
return min(values), max(values) # returns a tuple
low, high = min_max([5, 2, 9, 1])
print(low, high) # 1 9A dictionary maps keys to values, with fast lookup. Keys must be immutable.
# dictionaries.py
ages = {"Alice": 30, "Bob": 25}
print(ages["Alice"]) # 30
ages["Carol"] = 41 # add or update
print("Bob" in ages) # True: membership tests the keys
print(ages.get("Dan", 0)) # 0: a default when the key is missing
for name, age in ages.items():
print(name, age)Use dict.get() to avoid a KeyError when
a key might be absent.
A set is an unordered collection of unique items, with fast membership tests and the usual set algebra:
# sets.py
a = {1, 2, 3, 3} # duplicates collapse
b = {3, 4, 5}
print(a) # {1, 2, 3}
print(a & b) # {3}: intersection
print(a | b) # {1, 2, 3, 4, 5}: union
print(a - b) # {1, 2}: difference
print(2 in a) # TrueYou already saw if. Add elif for
chained tests, and note that Python’s comparison operators chain the
way they do in mathematics:
# control_flow.py
def classify(n):
if n < 0:
return "negative"
elif n == 0:
return "zero"
else:
return "positive"
print(classify(-3), classify(0), classify(7))
x = 5
print(0 < x < 10) # True: chained comparison
grade = "pass" if x >= 3 else "fail" # conditional expression
print(grade)A while loop runs until its condition is false.
break leaves the loop and continue skips
to the next iteration:
# while_loop.py
n = 27
steps = 0
while n != 1: # the Collatz sequence
n = n // 2 if n % 2 == 0 else 3 * n + 1
steps += 1
print(steps, "steps")For iteration, for walks any sequence directly. Use
range() for counting, enumerate() when you
also need the index, and zip() to walk two sequences
together:
# looping.py
for i in range(3): # 0, 1, 2
print(i, end=" ")
print()
names = ["Alice", "Bob", "Carol"]
for index, name in enumerate(names):
print(index, name)
scores = [88, 91, 79]
for name, score in zip(names, scores):
print(name, score)The match statement compares a value against
structural patterns. It is more than a C switch: a
pattern can destructure a value and bind its parts. (The
f"..." strings below are f-strings, covered
under Strings.)
# match_command.py
def run(command):
match command.split():
case ["go", direction]:
return f"moving {direction}"
case ["quit"]:
return "goodbye"
case _:
return "unknown command"
print(run("go north"))
print(run("quit"))
print(run("dance"))Python signals an error by raising an exception, which
propagates up until a try/except handles
it. Catch specific exception types, not everything:
# exceptions.py
def parse_int(text):
try:
return int(text)
except ValueError:
return None
print(parse_int("42")) # 42
print(parse_int("oops")) # None
def checked_divide(a, b):
if b == 0:
raise ValueError("cannot divide by zero")
return a / b
try:
checked_divide(1, 0)
except ValueError as e:
print("caught:", e)
finally:
print("finally always runs")An except clause names the exception type. An
optional else runs when no exception was raised, and
finally always runs, which makes it the place for
cleanup. Python’s culture leans on “easier to ask forgiveness than
permission”: try the operation and handle the exception, rather than
checking every precondition first.
A with block guarantees that setup and cleanup
happen as a pair, even if the body raises. Opening a file is the
canonical case: the file is closed on the way out, no matter
what:
# context_manager.py
import tempfile
from pathlib import Path
path = Path(tempfile.gettempdir()) / "demo.txt"
with path.open("w") as f:
f.write("one\ntwo\n") # f.close() happens automatically
with path.open() as f:
for line in f:
print(line.strip())
path.unlink()This is the explicit-finalizer approach from the Initialization and Cleanup chapter. Anything that acquires a resource (a file, a lock, a network connection) can be a context manager.
A comprehension builds a list, dictionary, or set from another sequence in one expression, replacing a loop that builds up a result:
# comprehensions_intro.py
squares = [n * n for n in range(5)] # list comprehension
print(squares) # [0, 1, 4, 9, 16]
evens = [n for n in range(10) if n % 2 == 0] # with a filter
print(evens) # [0, 2, 4, 6, 8]
lengths = {w: len(w) for w in ["a", "bb"]} # dict comprehension
print(lengths) # {'a': 1, 'bb': 2}This is such a core idiom that it has its own chapter, Comprehensions, which also covers
generator expressions and the functional tools map()
and filter().