How Python spells the things you already know, for the subset the early builds use. A read-once orientation, not a course.
No declarations or types up front. You assign and the name exists.
x = 5
name = "grid"
rate = 0.1
The ones the builds use: integers, floats, strings, booleans, and None for absence.
n = 5 # int
lr = 0.95 # float
s = "goal" # str
done = True # bool
nothing = None # absence of a value
An ordered, mutable sequence. Python's default container.
xs = [1, 2, 3]
xs.append(4)
first = xs[0]
how_many = len(xs)
Like a list but fixed once made. Handy for a pair such as a grid cell.
cell = (4, 0)
row, col = cell # unpack into two names
Key to value lookup. Useful for mapping or counting.
scores = {"a": 1, "b": 2}
scores["c"] = 3
val = scores["a"]
An unordered collection of unique items. Fast membership tests.
obstacles = {(1, 1), (2, 2)}
blocked = (1, 1) in obstacles # True
Square brackets read one element. Indexing starts at 0; negatives count from the end.
xs = [10, 20, 30]
xs[0] # 10
xs[-1] # 30
A range of elements with start:stop (stop is excluded).
xs = [0, 1, 2, 3, 4]
xs[1:3] # [1, 2]
xs[:2] # [0, 1]
xs[2:] # [2, 3, 4]
if, elif, else. The colon and indentation mark the block.
if x > 0:
sign = 1
elif x < 0:
sign = -1
else:
sign = 0
Iterate over a sequence directly, or over a number range. enumerate gives index and item.
for v in xs:
print(v)
for i in range(5): # 0,1,2,3,4
print(i)
for i, v in enumerate(xs):
print(i, v)
Repeat until a condition is false. Make sure something changes inside.
steps = 0
while not done:
steps = steps + 1
if steps > 100:
break
def names a function. Arguments can have defaults.
def step(cell, action, gamma=0.95):
# ... do something ...
return cell
Return a tuple and unpack it at the call site.
def env_step(cell, a):
return next_cell, reward, done
nxt, r, done = env_step(cell, a)
Bring in a module, optionally under a short alias.
import numpy as np
import random
from math import sqrt
Put values straight into a string with f"...{value}...".
ep = 12
print(f"episode {ep}: reward {total:.2f}")
Build a list in one line from a loop. Optional condition filters.
squares = [v * v for v in range(5)]
evens = [v for v in xs if v % 2 == 0]
Open a file with with, so it closes itself. Read all text or loop lines. Pass encoding="utf-8" so non-English characters load correctly rather than turning into garbage.
with open("corpus.txt", encoding="utf-8") as f:
text = f.read()
with open("corpus.txt", encoding="utf-8") as f:
for line in f:
print(line.strip())
A string loops one character at a time, and you join characters back with "".join(...). To walk adjacent pairs (the core move in a BPE tokenizer), zip a sequence against itself shifted by one. To pick the dictionary key with the largest value, pass key=d.get to max.
for ch in "hello": # 'h', 'e', 'l', 'l', 'o'
print(ch)
text = "".join(["h", "i"]) # 'hi'
syms = ["l", "o", "w", "e", "r"]
for a, b in zip(syms, syms[1:]): # ('l','o'), ('o','w'), ('w','e'), ('e','r')
print(a, b)
counts = {"lo": 5, "ow": 2, "er": 9}
best = max(counts, key=counts.get) # 'er' (the key with the highest count)
If you would rather not build the count dict by hand, collections.Counter does it for you:
from collections import Counter
counts = Counter(zip(syms, syms[1:])) # {('l','o'): 1, ('o','w'): 1, ...}
best, n = counts.most_common(1)[0] # the most frequent pair and its count
When something is wrong, look rather than guess. Print the value and, for arrays, the shape; read the bottom line of any traceback first; check you are in the right folder and that your imports succeeded.
print("Q row:", Q[s])
print("shape:", Q.shape)
numpy gives you fast arrays. The handful the early builds use:
import numpy as np
a = np.zeros((5, 4)) # a 5 by 4 array of zeros
a.shape # (5, 4)
a[0, 1] # one element (row 0, col 1)
a[0] # a whole row
a + b # elementwise add (same shapes)
a * 2 # multiply every element by 2
np.argmax(a[0]) # index of the largest value in row 0
np.max(a[0]) # the largest value in row 0
a.sum() # add everything up (also np.sum(a))
a @ b # dot product / matrix multiply (also np.dot(a, b))
np.linalg.norm(v) # length of a vector
M.mean(axis=0) # average down the rows, one value per column
M @ q # score a (D,) vector q against every row of an (N, D) matrix
np.argsort(scores) # indices that would sort the array (ascending)
X, Y = np.meshgrid(xs, ys) # a 2D grid of coordinates from two 1D arrays
Z = f(X, Y) # evaluate a surface at every grid point (vectorised)
The plotting builds (B3 onward) use matplotlib for a scatter, a line, or a contour map. The handful you need:
import matplotlib.pyplot as plt
plt.scatter(xs, ys) # points; xs and ys are equal-length arrays
plt.plot(xs, ys) # a line instead of points
plt.contour(X, Y, Z, levels=30) # contour lines of a surface Z over a grid
plt.contourf(X, Y, Z) # filled version (optional)
plt.colorbar() # a scale beside the plot (optional)
plt.xlabel("dim 1") # axis labels
plt.ylabel("dim 2")
plt.title("2D projection")
plt.savefig("plot.png") # write the figure to a file
plt.show() # open a window (skip this if you only save)