Skip to main content

Module 1 — The Python syntax that actually matters

Python is a large language, but data science uses a surprisingly compact subset of it. This module covers exactly that subset — the one you will read and write every day — and deliberately leaves out the corners you can learn later, or never.

Variables and types: dynamic typing, with its consequences

Python attaches types to values, not to variables. The same variable can hold an integer and then a string — the language does not object.

amount = 1250          # int
amount = 1250.75 # float — no error
client_name = "Aisha" # str
active = True # bool

This flexibility is what makes Python fast to write, and it has a price: type errors show up at runtime, when the faulty line executes. In data work, the classic manifestation is the numeric column read as text: "1250" + 10 raises an error — but only when you get there.

The four base types to know: int, float, str, bool — plus None, the "no value" value, everywhere in real data. Two survival functions: type(x) tells a value's type; int("42"), float("3.14"), str(42) convert explicitly.

The float trap

0.1 + 0.2 == 0.3 is False in Python — as in almost every language, because floats are binary approximations. To compare floats, test a gap: abs(a - b) < 1e-9. For money, consider integer cents. This detail causes real bugs in real pipelines.

f-strings: the only modern way to format

Every output, every log message, every chart label goes through f-strings:

precision = 0.9273
n = 15420
print(f"Precision: {precision:.2%} on {n:,} examples")
# Precision: 92.73% on 15,420 examples

The specifiers you will use constantly: :.2f (two decimals), :.2% (percentage), :, (thousands separator), :>10 (alignment). If you meet "..." % x or "...".format(x) in old code, it is the same thing, less readable.

Conditions: indentation is the syntax

Python delimits blocks with indentation, not braces. Four spaces per level is the universal convention.

if score >= 0.9:
verdict = "excellent"
elif score >= 0.7:
verdict = "acceptable"
else:
verdict = "needs work"

Two idioms worth knowing because they are everywhere:

# Conditional expression (ternary)
status = "adult" if age >= 18 else "minor"

# Implicit truthiness: empty list, empty string, 0 and None are "falsy"
if invalid_rows:
print(f"{len(invalid_rows)} rows to fix")

Comparison to None is always written is None / is not None, never == None.

Loops: for over anything iterable

Python's for loop walks the elements directly — not the indices:

for file in csv_files:
process(file)

# Need the index? enumerate
for i, column in enumerate(columns):
print(f"{i}: {column}")

# Walk two sequences together? zip
for true, pred in zip(y_test, y_pred):
if true != pred:
errors += 1

range(n) generates the integers 0 to n−1 when a counter is genuinely needed. break exits the loop, continue skips to the next iteration.

Above all, remember this, which prepares the NumPy module: in data science, a for loop over bulk data is almost always the wrong tool. Loops are for iterating over files, columns, hyperparameters — not for computing over millions of rows. Computation gets vectorized.

Slicing: the notation that shows up everywhere

The [start:stop:step] syntax applies to strings and lists — and reappears identically in NumPy and pandas, hence its importance:

text = "prediction_2026.csv"
text[0:10] # 'prediction' — stop excluded
text[-4:] # '.csv' — negative indices: from the end
text[:10] # implicit start
values[::2] # every other element
values[::-1] # reversed sequence

The rule to memorize: the stop index is excluded, and [a:b] contains exactly b - a elements. This convention, confusing on day one, makes slices composable: s[:k] + s[k:] == s.

Reading errors: the underrated skill

A Python traceback reads from the bottom up: the last line gives the error type and message; the lines above walk the call stack up to the faulty line.

ErrorTypical cause in data work
TypeErrorOperation between incompatible types — often a text column believed numeric
ValueErrorType is fine, value is not: int("abc")
KeyErrorMissing dictionary key — or missing DataFrame column
IndexErrorIndex out of bounds
FileNotFoundErrorWrong path — check the current working directory

The minimal debugging reflex: print the type and the value (print(type(x), repr(x))) just before the line that breaks. It solves an embarrassing share of problems.

Key takeaways

  • Dynamic types: flexibility is paid for with runtime errors — know int, float, str, bool, None and explicit conversions.
  • f-strings for all formatting; :.2f, :.2% and :, cover most needs.
  • Indentation = syntax; is None to test absence; empty containers are falsy.
  • for with enumerate and zip for orchestration — never for bulk computation, which gets vectorized (module 4).
  • Slicing [start:stop:step], stop excluded, is the shared notation of Python, NumPy and pandas.

Next module: data structures — lists, dictionaries, tuples, sets — and the comprehensions that transform them in one line.