Kiru Lab / Foundations: Python as an Instrument / Python From First Principles
Numbers, Text, and Truth
The four basic types, how to convert between them, and the floating-point fact that will bite you in every later track.
Concept · about 30 minutes
Python has a handful of basic types and you will use four of them constantly: integers, floating-point numbers, strings of text, and booleans. Knowing which one you are holding prevents most early confusion.
count = 12 # int — whole number, exact
learning_rate = 0.01 # float — decimal, approximate (see below)
name = "gradient" # str — text, in quotes
converged = True # bool — True or False, capitalized
print(type(count), type(learning_rate), type(name), type(converged))Converting between them
Python will not silently convert types for you, which is a feature. `"3" + 4` is an error rather than a guess, because the interpreter cannot know whether you meant 7 or "34". Convert explicitly and the ambiguity disappears.
int("42") # 42 — string to integer
float("3.14") # 3.14 — string to float
str(42) # "42" — number to string
int(3.9) # 3 — truncates toward zero, does NOT round
round(3.9) # 4 — this rounds
bool(0) # False — 0, "", [], and None are falsy; almost everything else is truthyFormatting text
An f-string lets you drop values directly into text. It is the only string formatting you need to learn, and you will use it in every print statement and log line you ever write.
loss = 0.03847
epoch = 12
print(f"epoch {epoch}: loss {loss}") # epoch 12: loss 0.03847
print(f"epoch {epoch}: loss {loss:.3f}") # epoch 12: loss 0.038 <- 3 decimals
print(f"epoch {epoch}: loss {loss:.2e}") # epoch 12: loss 3.85e-02The floating-point fact
Floats are stored in binary and most decimal fractions have no exact binary representation, exactly as one third has no exact decimal representation. So arithmetic on them accumulates tiny errors.
0.1 + 0.2 # 0.30000000000000004
0.1 + 0.2 == 0.3 # False
# So never compare floats with ==. Compare with a tolerance:
abs((0.1 + 0.2) - 0.3) < 1e-9 # TrueEvery number in a neural network and every joint angle in a robot arm is a float. Accumulated floating-point error is why gradient checks compare within a tolerance rather than for equality, why GPU results are not bitwise reproducible across machines, and why an ill-conditioned matrix is dangerous. You are meeting the constraint here in its simplest form.
Hold on to
- Python does not silently convert types — convert explicitly
- f-strings are the only formatting syntax you need
- Never compare floats with ==; compare within a tolerance
Work through
Try each one before opening the solution. Getting it wrong first is most of where the learning happens.
-
Predict the output of `int("7") + int(3.9)` before running it, then run it. Explain any difference between your prediction and the result.
Hint
`int()` on a float does not round.
Solution
The answer is 10. `int("7")` is 7, and `int(3.9)` truncates toward zero to 3, not 4. Most people predict 11 because they expect rounding. Use `round()` when you mean rounding — this distinction causes real off-by-one bugs in indexing code.
int("7") + int(3.9) # 10 int("7") + round(3.9) # 11 int(-3.9) # -3 — truncates toward zero, not down -
Write a function `close_enough(a, b, tol=1e-9)` that compares two floats safely, and show that it returns True for `0.1 + 0.2` and `0.3` where `==` returns False.
Hint
Compare the absolute difference against a small tolerance.
Solution
Take the absolute difference and check it is below a tolerance. Python also ships `math.isclose`, which handles relative tolerance for large values and is what you should reach for in real code.
def close_enough(a, b, tol=1e-9): return abs(a - b) < tol # The standard-library version, which also scales with magnitude: import math math.isclose(0.1 + 0.2, 0.3)Check your work
Paste this after your own code. If it runs without raising, you have it.
assert (0.1 + 0.2) != 0.3 assert close_enough(0.1 + 0.2, 0.3) assert not close_enough(0.1, 0.2) print("ok") -
Using an f-string, print a learning rate of 0.000125 in scientific notation with two decimal places, and a percentage of 0.8734 as "87.3%".
Hint
The format spec goes after a colon inside the braces: {value:.2e}.
Solution
Use `:.2e` for scientific notation and `:.1%` for a percentage — the percent format multiplies by 100 and appends the sign for you, so do not multiply first.
lr = 0.000125 acc = 0.8734 print(f"{lr:.2e}") # 1.25e-04 print(f"{acc:.1%}") # 87.3%Check your work
Paste this after your own code. If it runs without raising, you have it.
assert f"{0.000125:.2e}" == "1.25e-04" assert f"{0.8734:.1%}" == "87.3%" print("ok")
Sign in to track your progress through the lab.