Title: Lecture 2 — Variables, Data Structures, Functions, and Running Python Like You Mean It
Course: AIINFRA 100: AI Systems & Development Environments > Section: Week 05 — Python for AI — Fundamentals

📖 Lecture — Variables, Data Structures, Functions, and Running Python Like You Mean It

In Week 1 you stood up your AI dev environment. This week you start actually writing Python — and we're going to build the mental model that will make everything you do in this course, from Week 10's NumPy arrays onward, click into place.

Variables are names, not boxes

Many beginners picture a variable as a labeled box: write x = 5, and you imagine a box named x with a 5 inside it. That mental model works fine for numbers, but it quietly breaks the moment you start working with lists, arrays, or dataframes — which is most of what you'll do in AI work. The more accurate picture: a variable is a name (a label, a sticky note) that points to an object living in memory. When you write x = 5, Python creates the integer object 5 somewhere in memory and attaches the label x to it. When you then write y = x, you are not copying the value into a new box — you are sticking a second label, y, onto the same object. For immutable things like numbers and strings this distinction is invisible, because you can't change the object itself — any "change" actually creates a new object and re-points the label. But for mutable objects like lists, the difference is very visible:

scores = [10, 20, 30]
backup = scores       # backup is just another label on the SAME list
backup.append(40)
print(scores)          # [10, 20, 30, 40]  <- scores changed too!

Nothing here is a bug — scores and backup were always the same object with two names. This is exactly the behavior you'll rely on (and occasionally get bitten by) when you pass a list of features or a dataframe into a function later in this course. When you truly want an independent copy, you ask for one explicitly (e.g., backup = scores.copy()).

The four core data structures

Python ships with four built-in containers you'll use constantly. Each one exists for a different job:

Structure | Ordered? | Mutable? | Duplicates? | Typical AI use
list [ ] | Yes | Yes | Yes | A list of feature values, a batch of file names, a column of data
tuple ( ) | Yes | No | Yes | A fixed (x, y) coordinate or an image's (width, height) that shouldn't change
dict {key: value} | Yes (insertion order) | Yes | Keys are unique | A config ({"learning_rate": 0.01}), a JSON-like record, a skill → hours mapping
set { } | No | Yes | No — automatically unique | De-duplicating labels, checking membership fast

In AI and data work, you will lean on lists and dicts far more than tuples or sets. A row of data often arrives as a dict (mirroring JSON), and a batch of rows is usually a list of dicts. Get comfortable creating, indexing, and looping over both:

skills = ["Python", "Git", "SQL"]                 # list
hours_per_skill = {"Python": 5, "Git": 2, "SQL": 3}  # dict

for skill in skills:
    print(skill, "->", hours_per_skill[skill], "hrs/week")

Functions: packaging logic so you (and libraries) can reuse it

A function lets you name a chunk of logic once and call it repeatedly with different inputs. The anatomy:

def summarize(name, skills_dict, greeting="Hello"):
    total_hours = sum(skills_dict.values())
    return f"{greeting}, {name}! You're planning {total_hours} hrs/week across {len(skills_dict)} skills."

- def starts the definition; summarize is the name you'll call later.
- name and skills_dict are parameters — placeholders filled in when the function is called.
- greeting="Hello" is a default argument — if the caller doesn't supply greeting, it falls back to "Hello".
- return sends a value back to whoever called the function. Without return, a function silently gives back None.

This pattern — inputs in, a value out — is exactly how the library functions you'll meet soon behave: numpy.array(...), pandas.read_csv(...), and model.fit(X, y) are all just functions (or methods) with parameters and return values, written by someone else, that you call the same way you'll call your own.

Indentation is syntax, not decoration

In most languages, whitespace is cosmetic. In Python, indentation defines where a block starts and ends. The colon at the end of an if, for, or def line tells Python "an indented block follows":

def check_score(score):
    if score >= 60:
        print("Pass")
    else:
        print("Try again")

Every line inside the if block is indented one level further than the if itself. Mixing tabs and spaces, or indenting inconsistently, causes an IndentationError — one of the most common errors beginners hit. VS Code is configured by default to insert spaces automatically when you press Tab, which avoids most of this pain. Stick with 4 spaces per indent level and let your editor do the work.

Running a script two ways

You will run Python code in two ways throughout this course, and you should understand both:

1. From the terminal: python student_profile.py (or python3 on some systems) runs the file using whatever Python interpreter is currently active on your PATH — which, if you've activated a virtual environment, is the venv's interpreter.
2. VS Code's Run button (or Run/Debug panel): VS Code runs the file using the interpreter it has selected for the project (shown in the bottom status bar). This is usually the same venv, but not always — if VS Code is pointed at a different interpreter than your terminal, you can get confusing "it works here but not there" moments.

The fix is simple: always check which interpreter is active in both places before you start debugging a "why doesn't this work" problem. In VS Code, click the interpreter name in the status bar to confirm or change it.

Virtual environments: one clean toolbox per project

A virtual environment (venv) is an isolated, self-contained copy of Python plus its own private folder for installed packages. Creating one for a project means packages you pip install land inside that project's venv — not in your system-wide Python. Why introduce this now, in Week 5, rather than waiting? Later in this course you'll start installing real AI libraries (NumPy, pandas, and friends). Without a venv, all of those packages get dumped into your one global Python install. That causes two problems down the road: different projects can silently conflict over which version of a package they need, and it becomes very easy to "corrupt" your system Python with a tangle of unrelated packages you can no longer account for. A venv gives every project its own clean toolbox. A common misconception is that running pip install numpy installs NumPy "for Python, everywhere on the computer." In reality, pip install only affects whichever Python/venv is currently active. That's precisely why, later, a script can fail with ModuleNotFoundError: No module named 'numpy' even though you know you installed it — you installed it into a different environment than the one that's currently running. Making a habit now of always activating your venv before installing or running anything will save you real frustration in Week 10 and beyond.