Python Programming From Zero

01

7-Day Sprint

Seven days, one key concept each, with a small challenge at the end of every day.

Do one day at a time and actually type the code instead of reading it, because typing is where the learning happens. Each day builds directly on the one before, so finish the challenge before moving on, even if your solution is ugly.

Day 1

Running Code and Storing Values

A Python program is a list of instructions run top to bottom, and variables are named boxes that hold values you want to reuse.

Install Python from python.org, then open a terminal and type python3 (or python on Windows) to get an interactive prompt where each line you type runs immediately. That prompt is great for experiments, but real programs live in a file ending in .py that you run with python3 myfile.py. Python reads that file from the first line to the last and does exactly what each line says, in order.

The two things you need today are print and variables. print("hello") displays text on the screen. A variable is created the moment you assign to it: name = "Ana" stores the text Ana under the label name, and age = 34 stores a number. From then on, writing name gives you back that value. Assignment always goes right to left, so total = total + 1 means take the current total, add one, and put the result back in total.

Python cares about types. "34" with quotes is a string, which is text; 34 without quotes is an integer; 34.5 is a float. You can add two numbers, and you can join two strings with +, but adding a number to a string raises a TypeError. Use f-strings to mix them cleanly: f"{name} is {age}" produces Ana is 34. Errors are normal and are not a sign you are bad at this; read the last line of the error message first, because that is where Python names the actual problem.

Real-world example

You want a tip calculator for a dinner bill. In a file called tip.py you write bill = 84.50, then tip_rate = 0.18, then tip = bill * tip_rate, then total = bill + tip, then print(f"Tip: {tip:.2f}, Total: {total:.2f}"). Running python3 tip.py prints Tip: 15.21, Total: 99.71, and changing only the first line updates everything else.

Today's challenge

Write a file that stores your first name, your city, and the number of hours you slept last night in three variables, then prints one sentence using an f-string that includes all three. Then deliberately try to write "5" + 5, read the error message, and fix it by converting with int("5").

Day 2

Making Decisions With if

Comparisons produce True or False, and if, elif, and else let your program take different paths depending on those values.

Programs get useful when they react to what they find. Python has comparison operators: == for equal, != for not equal, and <, >, <=, >= for ordering. Each comparison evaluates to a boolean, either True or False. Note the double equals; a single = is assignment, and mixing them up is the most common beginner bug.

An if statement runs a block only when its condition is True. Write if temperature > 30:, then indent the next lines by four spaces. Indentation is not decoration in Python, it is the syntax that defines which lines belong inside the block. Add elif for further conditions checked only if earlier ones failed, and else for the catch-all. Python checks the branches in order and runs at most one of them.

Combine conditions with and, or, and not. if age >= 18 and has_id: is True only when both are True. Python also treats some values as falsy when used as a condition: zero, an empty string, an empty list, and None all behave like False, so if name: means "if name is not empty". Keep conditions readable; if you need three ands, consider storing part of it in a well-named variable like is_eligible.

Real-world example

You are sorting a support inbox. You write priority = 0, then if "refund" in subject: priority = 2, then elif "password" in subject: priority = 1, then else: priority = 0, then print a message naming the priority. With subject = "Refund request for order 812" the check for the lowercase word refund fails, which teaches you to write subject.lower() first, and then it correctly assigns priority 2.

Today's challenge

Write a program that stores a number of minutes you exercised and prints "rest day" for 0, "light" for 1 to 29, "solid" for 30 to 59, and "big day" for 60 or more. Test it with 0, 29, 30, and 90 and make sure every case prints exactly one line.

Day 3

Lists and Loops

A list holds many values in order, and a for loop runs the same block of code once for each item in it.

A list is written with square brackets: prices = [4.5, 12.0, 3.25]. Items are indexed from zero, so prices[0] is 4.5 and prices[-1] is the last item. len(prices) tells you how many there are. Lists are mutable, meaning you can change them in place with prices.append(8.0) to add to the end, prices.remove(12.0) to delete a value, and prices[1] = 13.0 to overwrite a slot. Slicing gives you a sub-list: prices[0:2] returns the first two items.

The for loop is how you visit every item without repeating yourself. Write for price in prices: and indent the body; the variable price takes each value in turn. When you need a running result, create it before the loop: total = 0, then inside the loop total = total + price. That pattern, called an accumulator, covers counting, summing, and finding a maximum. When you need the position too, use enumerate(prices), which hands you both the index and the value.

Use a while loop when you do not know the number of repetitions in advance, only the stopping condition, and always make sure something inside the loop moves you toward that condition or you get an infinite loop. Two shortcuts worth learning today: range(5) produces the numbers 0 through 4 for counted loops, and a list comprehension like [p * 1.2 for p in prices] builds a new list from an old one in a single readable line.

Real-world example

You have a list of weekly grocery totals, spend = [62.40, 58.15, 71.00, 49.99]. You loop with total = 0 then for amount in spend: total += amount, then print(f"Spent {total:.2f} over {len(spend)} weeks, average {total/len(spend):.2f}"). It prints Spent 241.54 over 4 weeks, average 60.39, and adding a fifth week to the list requires no change to the loop.

Today's challenge

Given names = ["ana", "BEN", "Cleo", "dev"], write a loop that prints each name capitalized and numbered, like "1. Ana". Then, without a loop, build a second list containing only the names longer than three letters using a list comprehension.

Day 4

Functions

A function is a named, reusable block of code that takes inputs as parameters and hands back a result with return.

Copy-pasted code is a maintenance trap: fix a bug in one copy and the others stay broken. A function solves this. Define one with def, a name, parentheses listing the parameters, and a colon, then indent the body: def celsius_to_f(c): followed by return c * 9 / 5 + 32. Nothing happens when you define it; the code runs only when you call it, as in celsius_to_f(20), which evaluates to 68.0.

The word return is what sends a value back to the caller and immediately ends the function. A function without return still gives back something, the special value None, which is why print(some_function()) sometimes shows None. Keep the difference straight: print shows something to a human, return hands a value to the rest of your program. Only returned values can be stored in variables and used in later calculations.

Parameters are local to the function, so changing them inside does not touch the caller's variables. You can give a parameter a default, as in def greet(name, greeting="Hello"):, making greeting optional. Aim for functions that do one thing and have a name that says what they do, like average_of or is_valid_email. When a function needs a short description, put a triple-quoted docstring on the first line of the body so a future reader knows the intent.

Real-world example

You are checking passwords for a signup form. You write def is_strong(pw): with a body that returns False if len(pw) < 10, returns False if pw.isalpha() is True, and otherwise returns True. Now you can call is_strong("kitten") for False, is_strong("kittenkitten") for False because it is all letters, and is_strong("kitten2024x") for True, without rewriting the rules each time you need them.

Today's challenge

Write a function named summarize that takes a list of numbers and returns a three-item result: the count, the total, and the average. Call it on [3, 9, 4] and print the results, then call it on an empty list and handle that case so it does not crash with a division by zero.

Day 5

Dictionaries and Structured Data

A dictionary stores values under meaningful keys instead of numeric positions, which is how real-world records are modeled.

A list is right when order matters and items are interchangeable. A dictionary is right when each value has a name. Write it with braces and colons: user = {"name": "Ana", "age": 34, "city": "Lisbon"}. You read a value with user["name"] and add or change one with user["age"] = 35. Keys are usually strings, and looking up a missing key raises a KeyError, so use user.get("phone") when a value may be absent; it returns None instead of crashing, or a fallback you supply as a second argument.

Looping over a dictionary gives you keys by default, but for key, value in user.items(): is what you usually want. The real power comes from nesting: a list of dictionaries is the standard shape for a table of records, one dictionary per row. With users as a list of such dictionaries, for u in users: print(u["name"]) walks the table, and you can filter with an if inside the loop.

Dictionaries also make excellent counters and lookup tables. Counting words means walking the words and doing counts[word] = counts.get(word, 0) + 1, which either starts at zero or increments what is there. When you need results in order, sorted(counts.items(), key=lambda pair: pair[1], reverse=True) gives you the pairs sorted by count, highest first. This combination of lists, dictionaries, loops, and functions is enough to solve a very large share of everyday programming tasks.

Real-world example

You track a reading habit as books = [{"title": "Dune", "pages": 412, "done": True}, {"title": "Ulysses", "pages": 730, "done": False}]. To report finished pages you write total = 0 then for b in books: if b["done"]: total += b["pages"], then print(total), which gives 412. Adding a new book means appending one dictionary, and the reporting code keeps working unchanged.

Today's challenge

Build a list of at least four dictionaries describing movies with keys title, year, and minutes. Print the title of the longest movie, print how many were released before 2010, and then count how many movies fall in each decade using a dictionary as a counter.

Day 6

Files, Modules, and Errors

Real programs read and write files, borrow tested code from modules, and stay alive when something goes wrong.

To read a file safely, use with open("notes.txt") as f: and then text = f.read() inside the block; the with statement closes the file for you even if an error occurs. Looping with for line in f: reads one line at a time, which matters for large files. To write, open with mode "w" to overwrite or "a" to append, then call f.write("a line\n"). Remember that write does not add the newline itself.

The standard library saves you from reinventing things. Import a module with import csv or from pathlib import Path. The csv module reads spreadsheet exports into rows, json turns Python dictionaries into text files and back with json.dumps and json.load, datetime handles dates and durations correctly, and random gives you shuffles and choices. When you need something outside the standard library, install it with pip in a virtual environment created by python3 -m venv .venv so each project keeps its own dependencies.

Things will fail: a missing file, a user typing letters where you wanted a number, a division by zero. Wrap the risky lines in try: and follow with except FileNotFoundError: to handle that specific failure, printing a helpful message or falling back to a default. Catch specific exception types rather than a bare except, because swallowing every error hides real bugs. You can also raise your own with raise ValueError("minutes must be positive") when a caller hands your function nonsense, which fails loudly and early instead of producing silent garbage.

Real-world example

You export your podcast subscriptions to feeds.txt, one URL per line, and some lines are blank. You write with open("feeds.txt") as f: lines = [line.strip() for line in f if line.strip()], which gives a clean list. You wrap the whole thing in try and except FileNotFoundError: print("No feeds file yet, starting empty") and set lines = [], so the first run of the program on a new machine works instead of crashing.

Today's challenge

Write a program that reads a text file of your choice, counts how many lines and how many words it contains, and appends a summary line to a second file called report.txt. Then rename the input file so it is missing, run the program again, and add a try/except that prints a clear message instead of a traceback.

Day 7

Building and Debugging a Small Program

Competence is assembling the pieces into one program with a clear structure, then narrowing bugs down by testing your assumptions.

A workable structure for a small script is this: functions at the top, each doing one job; then a main function that reads input, calls those functions, and prints output; then the line if __name__ == "__main__": main() at the bottom, which runs main only when the file is executed directly rather than imported. Take input from the command line with sys.argv or from input() prompts, and validate it before using it.

Build in slices, not all at once. Get one hardcoded value working end to end, print the intermediate results, then replace the hardcoded value with real input, then add the second feature. If you write fifty lines before running anything, you will have five bugs tangled together. Running after every few lines means each bug appears alone, which makes it obvious.

Debugging is a method, not a talent. Read the traceback from the bottom up to find the error type and the line number. Then print the variables just before that line to see whether they hold what you assumed; nine times out of ten the value is a string where you expected a number, a list that is empty, or None returned from a function that forgot to return. Reduce the problem to the smallest input that still fails. Finally, write your assumptions down as checks: a tiny function called with a known input and compared to a known answer with assert average([2, 4]) == 3 catches the day you break it while changing something else.

Real-world example

You build expenses.py that reads a CSV of date, category, amount rows and prints a monthly total per category. You start with a hardcoded three-row list to get the grouping dictionary right, then swap in csv.DictReader, and immediately hit a TypeError because amount arrives as the string "12.40". You print the value, see the quotes, add float(row["amount"]), and the totals come out right; then you add a check that skips rows where the amount is missing.

Today's challenge

Build a command-line habit tracker in one file. It should load entries from a JSON file if it exists, let you add an entry with a date and a habit name, save back to the file, and print a summary of how many times each habit appears. Use at least three functions, handle the missing-file case, and add one assert that verifies your counting function on a small fixed input.