12  Python Fundamentals

In Unit 1, you learned to work with your computer through the command line. In Unit 2, you learned to query and transform data with SQL. Now you’ll learn Python, the language that ties everything together.

This chapter sets up your Python environment, introduces the core building blocks of the language, and highlights the places where Python’s design differs from what you might expect. If you’ve written code in Matlab or another language before, some of this will feel familiar. Pay close attention to the sections on truthiness and short-circuit evaluation. They’re the parts that trip up experienced programmers coming from other languages, and Python leans on these behaviors heavily.

12.1 Why Python?

Every tool you’ve learned so far has a specific job. SQL retrieves and transforms data inside a database. Git tracks changes to files over time. The command line navigates your file system and runs programs. Each tool is powerful within its domain, but none of them can do everything.

Python is different. It’s a general-purpose programming language, which means it wasn’t designed for any single task. Instead, it’s designed to be flexible enough to handle almost anything: reading files, connecting to databases, building web applications, training machine learning models, automating reports, and more. Python isn’t the best tool for any one of these tasks, but it’s good enough at all of them, and it excels at connecting specialized tools together into complete workflows.

Consider a realistic engineering task: every Monday morning, you need to pull last week’s production data from a database, compute quality metrics, flag any lines that fell below threshold, generate a summary report, and email it to your team. SQL can pull the data. Excel can compute the metrics. Outlook can send the email. But who connects those steps? That’s Python’s job. It orchestrates the workflow from start to finish, handling the logic, file management, and automation that no single specialized tool can do alone.

This orchestration role explains why Python has become the common language across data science, data engineering, web development, and scientific computing. It’s not because Python is the fastest language (it isn’t) or the most mathematically elegant (Matlab and R have stronger claims there). It’s because Python is the language that connects everything else.

NoteWhere do other languages fit?

Matlab excels at numerical computation, matrix operations, and simulation. If your work is primarily solving systems of equations or running Simulink models, Matlab is the right tool. R excels at statistical analysis and publication-quality data visualization. If you’re doing formal hypothesis testing or building statistical models, R has deeper support. SQL excels at querying and transforming data inside databases. You learned this in the previous module, and you’ll continue using it alongside Python.

Python doesn’t replace any of these. It fills the gaps between them and handles the tasks none of them were designed for: file processing, automation, API integration, and workflow orchestration.

12.2 Setting Up Your First Project

In Unit 1, you used uv to manage tools. Now you’ll use it to manage Python itself. Install uv and Python 3.13 following the instructions in Appendix H. Verify the installation:

terminal
uv python list --only-installed

You should see Python 3.13 in the output.

12.2.1 Creating a Project

Every Python project starts with uv init. Navigate to where you want to create your project and run:

terminal
uv init my-project
cd my-project

This creates a directory with the following structure:

output
my-project/
├── pyproject.toml    # project metadata and dependencies
├── .python-version   # pins the Python version
├── README.md         # project documentation
├── .gitignore        # files Git should ignore
└── hello.py          # starter script

Each of these files serves a specific purpose. The .python-version file pins the exact Python version so that anyone who clones your project gets the same one. The .gitignore file tells Git to skip files that shouldn’t be tracked, like the virtual environment directory. The README.md is the front door of your project, a topic we’ll revisit in Chapter 22.

The most important file is pyproject.toml. Open it in Zed:

pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

This file describes your project: its name, version, which Python version it needs, and what external packages it depends on. For now, it’s mostly empty. In Chapter 22, you’ll learn to use pyproject.toml extensively. For now, just know that it exists and that uv reads it to manage your project.

12.2.2 Running Your First Script

Open hello.py in Zed. You’ll see:

hello.py
def main():
    print("Hello from my-project!")


main()

Run it from the terminal:

terminal
uv run hello.py
output
Hello from my-project!

The uv run command does several things behind the scenes: it creates a virtual environment if one doesn’t exist, installs any dependencies listed in pyproject.toml, and then executes your script using the correct Python version. You don’t need to activate environments manually or worry about which Python is being used. uv run handles all of it.

TipWhy uv run instead of python?

Running python hello.py directly uses whatever Python happens to be on your system PATH, which might be the wrong version or might not have your project’s dependencies installed. uv run hello.py always uses the right Python with the right packages. Make it a habit: always use uv run for this book.

12.3 Comments

Comments are lines in your code that Python ignores. They exist for humans, not for computers. In Python, comments start with the # character:

comments.py
# This is a comment. Python skips this line entirely.
x = 42  # Comments can also appear after code on the same line.

The purpose of comments is to explain why your code does something, not what it does. The code itself tells you what happens. The comment should tell you the reasoning behind it.

Here’s a bad comment:

bad_comment.py
x = x + 1  # increment x by 1

This comment restates the code. Anyone who can read Python already knows that x + 1 increments x. The comment adds nothing.

Here’s a good comment:

good_comment.py
# Convert PSI to kPa using the ISO 80000-4 conversion factor
pressure_kpa = pressure_psi * 6.894757

This comment explains the why: where the magic number 6.894757 comes from and what standard it references. Six months from now, you (or a colleague) will understand this line immediately instead of wondering whether the constant is correct.

12.3.1 Block Comments and Inline Comments

Block comments appear on their own line, above the code they describe. Inline comments appear on the same line as code, separated by at least two spaces:

comment_styles.py
# Calculate the weighted average cycle time across all stations.
# Stations with zero throughput are excluded to avoid division errors.
avg_cycle_time = total_weighted_time / active_stations  # in seconds

Block comments are better for explaining why a section of code exists. Inline comments are better for brief clarifications, like units or edge cases.

12.3.2 TODO and FIXME

Two conventional comment markers help you track work in progress:

markers.py
# TODO: Add support for multiple product categories
# FIXME: This breaks when the order quantity is zero

TODO marks something you plan to implement later. FIXME marks a known bug or problem. Most code editors, including Zed, highlight these markers so they’re easy to find. They’re a lightweight alternative to formal issue tracking for small projects.

12.3.3 When Code Is Self-Documenting

Well-chosen variable names often eliminate the need for comments entirely. Compare:

unclear.py
# Calculate the total cost including tax
t = p * q * (1 + r)
clear.py
total_cost = unit_price * quantity * (1 + tax_rate)

The second version needs no comment. The variable names tell the full story. When you find yourself writing a comment that restates the code, consider whether better naming would make the comment unnecessary.

12.4 The REPL

The REPL (Read-Eval-Print Loop) is Python’s interactive prompt. It reads an expression you type, evaluates it, prints the result, and loops back for more input. Start it from your project directory:

terminal
uv run python

You’ll see something like:

output
Python 3.13.2 (main, Feb  4 2025, 14:51:09) [Clang 19.1.6] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

The >>> prompt is where you type Python expressions. Try it:

Python REPL
>>> 2 + 3
5
>>> "hello" + " " + "world"
'hello world'
>>> 7 / 2
3.5
>>> 7 // 2
3

The REPL is a scratchpad. Use it when you want to test an expression, check how a function behaves, or explore an unfamiliar library. It’s faster than writing a script, running it, reading the output, and editing the script again.

That said, the REPL isn’t a substitute for scripts. Anything you type in the REPL vanishes when you close it. If your work needs to be saved, versioned with Git, or run again later, write a script. The REPL is for exploration; scripts are for production.

TipExiting the REPL

Type exit() or press Ctrl+D to leave the REPL and return to your terminal.

12.5 Variables and Types

A variable is a name attached to a value. In Python, you create a variable by assigning to it with =:

variables.py
product_name = "Widget A"
unit_price = 14.99
quantity = 250
in_stock = True

Python is dynamically typed, which means you don’t declare a variable’s type before using it. Python figures out the type from the value you assign. This is different from languages like C or Java, where you must declare types explicitly, and subtly different from Matlab, where everything is a matrix by default.

12.5.1 Core Types

Python has a small set of built-in types that cover most everyday needs:

Table 12.1: Python’s core scalar types
Type Description Examples
int Whole numbers 42, -7, 0, 1_000_000
float Decimal numbers 3.14, -0.001, 1e6
str Text (strings) "hello", 'world', ""
bool Boolean True, False
None The absence of a value None

You can check the type of any value using the type() function:

type_checking.py
type(42)          # <class 'int'>
type(3.14)        # <class 'float'>
type("hello")     # <class 'str'>
type(True)        # <class 'bool'>
type(None)        # <class 'NoneType'>
NoteUnderscores in numbers

Python lets you use underscores as visual separators in large numbers: 1_000_000 is the same as 1000000. This is purely for readability and has no effect on the value.

12.5.2 Integers and Floats

Python’s int type has no size limit. You can represent arbitrarily large whole numbers without worrying about overflow:

big_int.py
big_number = 2 ** 100
print(big_number)  # 1267650600228229401496703205376

Floats, on the other hand, follow the IEEE 754 standard for double-precision floating point. This means they have limited precision, and you’ll occasionally see rounding artifacts:

float_precision.py
0.1 + 0.2  # 0.30000000000000004

This isn’t a Python bug. It’s a fundamental property of how computers represent decimal numbers in binary. For most engineering calculations, the precision is more than sufficient. When exact decimal arithmetic matters (financial calculations, for example), Python provides the decimal module.

12.5.3 Strings

Strings are sequences of characters, enclosed in either single quotes or double quotes:

strings.py
name = "Northwind Traders"
greeting = 'Hello, world!'

Both forms are identical in behavior. Pick one convention and stick with it. Throughout this book, we’ll use double quotes for strings, matching the convention enforced by the Ruff formatter you’ll meet in Chapter 23.

Strings in Python are immutable: once created, they can’t be changed in place. Operations that appear to modify a string actually create a new one:

string_immutability.py
name = "widget"
upper_name = name.upper()  # Creates a new string "WIDGET"
print(name)                # Still "widget", unchanged

12.5.4 None

None is Python’s way of representing “nothing” or “no value.” If you’ve used SQL, think of None as Python’s equivalent of NULL. It’s not zero, not an empty string, and not False. It’s the explicit absence of a value:

none.py
result = None  # We haven't computed the result yet

You’ll use None frequently as a default value, a return value for functions that don’t produce a result, and a signal that something hasn’t been initialized. We’ll revisit None handling extensively in later chapters.

12.5.5 Variable Naming Conventions

Python has a strong convention for naming variables: use snake_case. All letters are lowercase, with words separated by underscores:

naming.py
# Good: snake_case
unit_price = 14.99
order_count = 42
is_discontinued = False

# Bad: other conventions
unitPrice = 14.99      # camelCase (Java/JavaScript convention)
OrderCount = 42        # PascalCase (reserved for class names in Python)
UNIT_PRICE = 14.99     # SCREAMING_SNAKE_CASE (reserved for constants)

This isn’t just style. It’s a signal. When you see snake_case, you know it’s a variable or function. When you see PascalCase, you know it’s a class. When you see SCREAMING_SNAKE_CASE, you know it’s a constant. These conventions make Python code readable across teams and projects.

12.5.6 Exercises

  1. Predict the output of type(True + 1) and type(1.0 + 2). Then test in the REPL. Why does Python return those types?

  2. A variable is assigned temperature = 72. Write expressions to convert it to Celsius and Kelvin using the formulas C = (F - 32) × 5/9 and K = C + 273.15. Use descriptive variable names.

  3. What is the difference between None, 0, "", and False? Write a short script that assigns each to a variable and prints type() for each. Are any of them equal to each other when compared with ==?

  4. Predict what happens when you run product_name = "Chai" and then product_Name (note the capital N). Why?

1. type(True + 1)<class 'int'> because True is treated as 1 in arithmetic. type(1.0 + 2)<class 'float'> because mixing float and int promotes to float.

2.

solution.py
temperature_f = 72
temperature_c = (temperature_f - 32) * 5 / 9
temperature_k = temperature_c + 273.15
print(f"{temperature_f}°F = {temperature_c:.2f}°C = {temperature_k:.2f}K")

3. They are all different types (NoneType, int, str, bool). 0 == False is True (bool is a subclass of int), and "" == False is False. None is not equal to any of the others.

4. NameError: name 'product_Name' is not defined. Python variable names are case-sensitive, so product_name and product_Name are completely different names.

12.5.7 Multiple Assignment

Python lets you assign multiple variables in a single statement:

multi_assign.py
x, y, z = 1, 2, 3

This also gives you a clean idiom for swapping values:

swap.py
a = 10
b = 20
a, b = b, a  # a is now 20, b is now 10

In most languages, swapping requires a temporary variable. Python handles it natively because the right side of the assignment is fully evaluated before any assignments happen.

12.6 Operators and Expressions

Python’s arithmetic operators work the way you’d expect, with a few additions that are worth knowing:

arithmetic.py
10 + 3    # 13    Addition
10 - 3    # 7     Subtraction
10 * 3    # 30    Multiplication
10 / 3    # 3.333... Division (always returns float)
10 // 3   # 3     Floor division (rounds down to int)
10 % 3    # 1     Modulo (remainder)
10 ** 3   # 1000  Exponentiation

Two operators deserve special attention. Floor division (//) divides and rounds down to the nearest integer. Modulo (%) returns the remainder after division. Together, they’re useful for tasks like converting units or cycling through indices:

divmod.py
total_minutes = 137
hours = total_minutes // 60    # 2
minutes = total_minutes % 60   # 17
print(f"{hours}h {minutes}m")  # 2h 17m
WarningDivision always returns a float

In Python, / always produces a float, even when dividing two integers that divide evenly: 10 / 2 returns 5.0, not 5. If you need an integer result, use //.

12.6.1 String Operations

Strings support concatenation with + and repetition with *:

string_ops.py
first = "North"
last = "wind"
full = first + last       # "Northwind"
separator = "-" * 40      # "----------------------------------------"

But the most useful string feature in Python is the f-string (formatted string literal). Prefix a string with f and put expressions inside curly braces:

fstrings.py
product = "Widget A"
price = 14.99
quantity = 250

summary = f"{product}: {quantity} units at ${price:.2f} each"
print(summary)  # Widget A: 250 units at $14.99 each

The :.2f inside the braces is a format specification: it formats the number as a float with exactly two decimal places. F-strings can contain any valid Python expression:

fstring_expressions.py
total = f"Total: ${price * quantity:,.2f}"
print(total)  # Total: $3,747.50

The :,.2f format adds comma separators and two decimal places, perfect for currency.

12.6.2 Comparison Operators

Comparison operators return True or False:

comparisons.py
10 > 5     # True
10 < 5     # False
10 >= 10   # True
10 <= 9    # False
10 == 10   # True   (equality check, not assignment)
10 != 5    # True   (not equal)
Warning== vs. =

A single = is assignment: x = 5 stores the value 5 in x. A double == is comparison: x == 5 checks whether x equals 5. Mixing these up is a common source of bugs.

Python also supports chained comparisons, a feature that reads like natural language:

chained.py
x = 7
1 < x < 10        # True (x is between 1 and 10)
1 < x < 5         # False
0 <= x <= 100     # True

This is equivalent to 1 < x and x < 10, but the chained form is more readable.

12.6.3 The in Operator

The in operator tests whether a value exists inside a collection:

in_operator.py
"a" in "apple"          # True
"z" in "apple"          # False
5 in [1, 2, 3, 4, 5]   # True

You’ll use in constantly in Python, especially with collections (Chapter 13) and loops.

12.6.4 Exercises

  1. Write an expression that computes the total cost of 250 units at $14.99 each, with a 6% sales tax. Format the result as currency using an f-string with comma separators and 2 decimal places.

  2. A production line runs for total_minutes = 497 minutes. Use floor division and modulo to compute the hours and remaining minutes. Print the result as "8h 17m" (with your actual computed values).

  3. Predict the result of each expression, then verify: 10 / 5, 10 // 5, 10 % 5, 7 % 2, 2 ** 10. Pay attention to types.

  4. Given name = "Northwind Traders", use the in operator to check if "wind" appears in the name. Then use string methods to check if the name starts with "North" and ends with "Inc".

1. f"Total: ${14.99 * 250 * 1.06:,.2f}""Total: $3,972.35"

2.

solution.py
total_minutes = 497
hours = total_minutes // 60
minutes = total_minutes % 60
print(f"{hours}h {minutes}m")  # 8h 17m

3. 10 / 52.0 (float, division always returns float), 10 // 52 (int), 10 % 50 (int), 7 % 21 (int), 2 ** 101024 (int).

4.

solution.py
name = "Northwind Traders"
"wind" in name              # True
name.startswith("North")    # True
name.endswith("Inc")        # False

12.7 Python’s Boolean System

This section covers one of the most important and most commonly misunderstood aspects of Python. Every value in Python, not just True and False, has a truthiness: it evaluates to either True or False when used in a boolean context like an if statement.

12.7.1 Truthy and Falsy Values

The following values are falsy, meaning they evaluate to False:

Table 12.2: Falsy values in Python
Value Type Why it’s falsy
False bool Obviously
0 int Zero is false
0.0 float Zero is false
"" str Empty string
[] list Empty list
{} dict Empty dictionary
() tuple Empty tuple
set() set Empty set
None NoneType No value

Everything else is truthy: it evaluates to True. Non-zero numbers, non-empty strings, non-empty collections, and most objects are all truthy.

truthiness.py
# All of these are truthy
bool(1)            # True
bool(-1)           # True (any non-zero number)
bool(0.001)        # True
bool("hello")      # True (any non-empty string)
bool(" ")          # True (a space is not empty!)
bool([0])          # True (a list with one element, even if that element is 0)

That last example is important: a list containing 0 is truthy because the list is non-empty. The truthiness of a collection depends on whether the collection itself is empty, not on the values it contains.

12.7.2 Short-Circuit Evaluation

Python’s and and or operators don’t just return True or False. They return one of their actual operands, and they use short-circuit evaluation, meaning they stop as soon as the result is determined.

and returns the first falsy value it encounters, or the last value if all are truthy:

and_operator.py
"hello" and "world"    # "world"  (both truthy, returns the last one)
"hello" and 0          # 0        (0 is falsy, returns it)
"" and "world"         # ""       (empty string is falsy, returns it)
0 and ""               # 0        (0 is falsy, stops immediately)

or returns the first truthy value it encounters, or the last value if all are falsy:

or_operator.py
"hello" or "world"     # "hello"  (first is truthy, returns it immediately)
"" or "world"          # "world"  (first is falsy, tries second)
0 or ""                # ""       (both falsy, returns the last one)
0 or None              # None     (both falsy, returns the last one)

This behavior enables a common Python pattern for default values:

default_value.py
user_input = ""  # Imagine the user left a field blank
name = user_input or "Anonymous"
print(name)  # "Anonymous"

Since the empty string is falsy, or moves on to the second operand and returns "Anonymous". If user_input had been "Alice", or would have returned "Alice" immediately without ever looking at the second operand.

WarningThe or default pattern and zero

Be careful with value or default when value might legitimately be 0 or 0.0:

or_gotcha.py
quantity = 0
result = quantity or 10  # 10, not 0!

Since 0 is falsy, or treats it the same as “missing” and returns 10. If 0 is a valid value, you need an explicit check instead:

explicit_check.py
result = quantity if quantity is not None else 10

12.7.3 The not Operator

not inverts truthiness:

not_operator.py
not True      # False
not False     # True
not 0         # True  (0 is falsy, so not 0 is True)
not "hello"   # False ("hello" is truthy, so not "hello" is False)
not None      # True  (None is falsy)

You’ll see not most often in conditions: if not results: is a concise way to check whether a list is empty.

TipConnecting to SQL

If you’re thinking that truthiness feels like SQL’s NULL handling, you’re right. SQL’s NULL propagates through expressions in surprising ways, and Python’s None has its own quirks. The pattern of “checking for nothingness” is a recurring theme across programming languages, and once you learn to watch for it, you’ll recognize it everywhere.

12.8 Putting It Together: A Northwind Product Summary

Let’s combine everything from this chapter into a single script. Imagine you’re building a quick summary for a product in the Northwind database:

product_summary.py
# Product data (in later chapters, we'll read this from files and databases)
product_name = "Chai"
category = "Beverages"
unit_price = 18.00
units_in_stock = 29
units_on_order = 0
discontinued = False
reorder_level = 48

# Compute derived values
needs_reorder = units_in_stock <= reorder_level and not discontinued
total_value = unit_price * units_in_stock

# Format the summary
status = "DISCONTINUED" if discontinued else "Active"
reorder_flag = " [REORDER NEEDED]" if needs_reorder else ""

print(f"{'=' * 40}")
print(f"Product: {product_name}")
print(f"Category: {category}")
print(f"Status: {status}{reorder_flag}")
print(f"Unit Price: ${unit_price:,.2f}")
print(f"In Stock: {units_in_stock} units")
print(f"On Order: {units_on_order} units")
print(f"Stock Value: ${total_value:,.2f}")
print(f"{'=' * 40}")
output
========================================
Product: Chai
Category: Beverages
Status: Active [REORDER NEEDED]
Unit Price: $18.00
In Stock: 29 units
On Order: 0 units
Stock Value: $522.00
========================================

This script uses variables, types, arithmetic operators, f-strings, comparison operators, boolean logic with and and not, and a conditional expression. It’s a small program, but it’s a complete one that produces useful output.

Exercises

Predict the Output

For each expression, predict what Python will return before testing it in the REPL. Then verify your prediction. Understanding why each result occurs matters more than getting the right answer.

predict.py
# 1
bool([])

# 2
bool([0, 0, 0])

# 3
"" or "default"

# 4
"hello" or "default"

# 5
0 and "hello"

# 6
1 and "hello"

# 7
None or 0 or "" or "finally"

# 8
not not "hello"

# 9
True + True + True

# 10
"hello" and "" and "world"

Unit Conversion Script

Write a script called conversions.py that performs the following engineering unit conversions. Use well-named variables and include a comment citing the source or standard for each conversion factor.

  1. Convert 1500 PSI to kPa and MPa.
  2. Convert 72°F to Celsius and Kelvin.
  3. Convert 5.5 miles to kilometers and meters.
  4. Convert 2000 pounds-force to Newtons and kilonewtons.

Print each result using f-strings with appropriate precision (2 decimal places).

REPL Exploration

Open the REPL with uv run python and explore the following questions. Record your findings in a script called exploration.py as comments.

  1. What happens when you add a string and an integer? ("hello" + 5)
  2. What does type(True + 1) return? What does True + 1 evaluate to?
  3. What is "hello" * 0? What about "hello" * -1?
  4. Can you chain more than two comparisons? Try 1 < 2 < 3 < 4.
  5. What does type(None) return?

Northwind Product Card

Extend the product summary script to include three products from the Northwind catalog. For each product, define variables for product_name, unit_price, units_in_stock, discontinued, and reorder_level. Print a formatted summary card for each one. Include a “REORDER NEEDED” flag for products whose stock is at or below the reorder level (but only if the product isn’t discontinued).

Use these products (values from the actual Northwind database):

Product Price In Stock Reorder Level Discontinued
Chai 18.00 29 48 No
Chang 19.00 68 46 No
Chef Anton’s Gumbo Mix 21.35 45 37 Yes

Summary

This chapter established your Python environment and introduced the core building blocks of the language. You set up a project with uv init, learned to run scripts with uv run, and explored the REPL as a tool for quick experimentation. Python’s core types, int, float, str, bool, and None, map closely to the column types you’ve seen in SQL, and you’ll work with them constantly.

The most important takeaway from this chapter is how Python handles boolean logic. Every value has a truthiness, and and/or return actual operands rather than just True or False. This behavior powers common patterns like value or default that you’ll see throughout Python code. Understanding truthiness now will save you hours of debugging later.

In the next chapter, you’ll move from individual values to collections: lists, tuples, dictionaries, and sets. These are the data structures that let you work with groups of values, and they’ll connect directly to the rows and columns you know from SQL.

Glossary

assignment
Storing a value in a variable using =. Example: x = 42.
boolean
A type with only two values: True and False. Used for logic and conditions.
comment
Text in source code that Python ignores, marked with #. Used to explain the reasoning behind code.
dynamic typing
A language feature where variables don’t have fixed types. The type is determined by the value currently assigned.
expression
A combination of values, variables, and operators that Python evaluates to produce a result. Example: 2 + 3.
f-string
A formatted string literal, prefixed with f, that can contain expressions inside curly braces. Example: f"Total: {x + y}".
falsy
A value that evaluates to False in a boolean context. In Python: 0, 0.0, "", [], {}, (), set(), None, and False.
float
A number with a decimal point, stored using IEEE 754 double-precision format.
floor division
Division that rounds the result down to the nearest integer, using the // operator.
integer
A whole number with no decimal point. Python integers have no size limit.
modulo
The remainder after division, computed with the % operator.
None
Python’s representation of “no value,” analogous to SQL’s NULL.
operator
A symbol that performs an operation on values. Arithmetic (+, -), comparison (==, <), and logical (and, or, not) are the main categories.
REPL
Read-Eval-Print Loop. An interactive Python prompt for testing expressions.
short-circuit evaluation
The behavior where and and or stop evaluating as soon as the result is determined. and stops at the first falsy value; or stops at the first truthy value.
snake_case
Python’s naming convention for variables and functions: lowercase words separated by underscores.
string
A sequence of characters, enclosed in single or double quotes. Strings are immutable.
truthy
A value that evaluates to True in a boolean context. Most non-zero, non-empty values are truthy.
variable
A name that refers to a value stored in memory.