1print("Hello, World!") # the classic first program2print("Welcome to Python.")
Explanation
Python is a high-level, general-purpose programming language created by Guido van Rossum and first released in 1991. "High-level" means it hides most of the low-level detail — memory management, machine instructions — so you can focus on solving the problem instead of managing the computer.
Its defining trait is readable syntax. Where many languages lean on curly braces and semicolons, Python uses indentation and plain keywords, so code tends to read close to English. That's a big reason it's often recommended as a first language.
The line print("Hello, World!") above calls Python's built-in print() function, which displays whatever is inside the parentheses on the screen. It's traditionally the first program anyone writes in a new language.
How to Use & Where is Used
How to use: Python code is run by an interpreter, which reads your code and executes it line by line — there's no separate compile step to run a script.
Where is used: Web backends, data analysis, AI and machine learning, automation scripts, and scientific computing.
Common in: Companies like Google, Netflix, and Instagram, plus most university intro-to-programming courses.
Example
The >>> below is the Python shell prompt — it means the code is typed interactively. Do not type >>> yourself; just type the code after it.
Typed into the interactive shell, Python runs each line the moment you press Enter and shows the result right away — useful for trying things out without writing a full file.
Practice Exercises
Exercise 1 — Say Hello:
Write a program that prints your own name, in the form Hello, my name is ___.
💡 Put the whole sentence inside one pair of quotes and pass it to print().
python
1print("Hello, my name is Alex")
Exercise 2 — Three Things:
Using three separate print() calls, print three of your favourite things, one per line.
💡 Each print() call starts on a new line automatically — just write three of them, one after another.
Exercise 3 — Do Some Math:
Print the result of 15 * 3 — without doing the multiplication yourself, let Python do it.
💡 You can put a math expression directly inside print() — no quotes needed for numbers.
python
1print(15 * 3) # 45
Quick Quiz
3 questions · Introduction
1. Who created Python?
Correct! Guido van Rossum created Python and released it in 1991.
Not quite — that's Guido van Rossum. The others created C, Java, and C++ respectively.
2. What does it mean that Python is a "high-level" language?
Correct! High-level means it hides memory management and hardware detail so you can focus on logic.
Not quite — "high-level" is about abstraction from hardware, not speed or release date.
3. Which of these is not a typical use case for Python?
Correct! Device drivers need direct hardware control, which is C/C++/Rust territory, not Python.
Not quite — Python is heavily used for data/ML, web backends, and automation. Device drivers need lower-level languages.
Quiz complete!
Chapter 1
2. Installation
Setting up Python on your machine.
Code
terminal
1$ python3 --version2Python 3.12.4
Explanation
Before you can run Python code, you need the Python interpreter installed. Download it from python.org/downloads — pick the latest stable 3.x release for your operating system.
Windows: run the installer, and make sure you tick Add python.exe to PATH on the first screen — this is what lets you type python from any terminal window.
macOS: macOS ships with an old system Python; install a current version from python.org or with Homebrew (brew install python) instead of relying on the built-in one.
Linux: most distributions already include Python 3 — check with the command below before installing anything.
The installer also sets up pip, Python's package manager, which you'll use later to install libraries other people have written.
How to Use & Where is Used
How to use: Open a terminal (Command Prompt / PowerShell on Windows, Terminal on macOS/Linux) and run python3 --version to confirm the install worked.
Where is used: The same interpreter you install here is what runs your scripts, powers your code editor's "Run" button, and is what production servers use to run Python applications.
Common in: A code editor like VS Code is the standard companion — it's free and has excellent Python support via an extension.
Example
Typing python3 alone (no filename after it) drops you into the interactive shell — a good way to sanity-check your install.
Once you see the >>> prompt, Python is installed and working. Type exit() and press Enter to leave the shell.
Practice Exercises
Exercise 1 — Confirm Your Install:
Open a terminal and print your installed Python version.
💡 On Windows try python --version; on macOS/Linux try python3 --version.
terminal
1$ python3 --version
Exercise 2 — Check pip:
Print the installed version of pip, Python's package manager.
💡 pip's version flag follows the same pattern as Python's own.
terminal
1$ pip3 --version
Exercise 3 — Enter and Exit:
Open the interactive shell, run 1 + 1, then leave the shell.
💡 python3 with nothing after it opens the shell; exit() closes it.
terminal
1$ python32>>>1 + 1324>>> exit()
Quick Quiz
3 questions · Installation
1. Where should you download Python from?
Correct! python.org is the official source for Python installers.
Not quite — always get Python from the official python.org site.
2. What does pip do?
Correct! pip is Python's package manager, used to install libraries.
Not quite — pip installs and manages third-party packages, it doesn't compile or format code.
3. Which command checks your installed Python version on macOS/Linux?
Correct! python3 --version prints the installed version.
Not quite — the flag is --version, run as python3 --version.
Quiz complete!
Chapter 1
3. Your First Program
Writing and running your first script.
Code
hello.py
1name = "Cynpho"2print("Hello,", name)3print("This is my first Python program.")
Explanation
So far you've typed code straight into the interactive shell, which runs each line immediately but forgets everything once you close it. A real program lives in a .py file — plain text saved with a .py extension — that you can run again any time and share with others.
The file above stores the text "Cynpho" in a variable called name (you'll cover variables properly in lesson 5), then uses two separate print() calls to display messages. Passing print() two things separated by a comma, like line 2 does, prints them on the same line with a space between.
You write .py files in a plain text editor or, better, a code editor with Python support like VS Code, which adds syntax highlighting and a built-in way to run the file.
How to Use & Where is Used
How to use: Save your code as hello.py, then in a terminal, navigate to that folder and run python3 hello.py.
Where is used: Every real Python project — from a ten-line script to a production web app — is just .py files run this same way.
Common in: Most editors also have a "Run" button that does the same python3 file.py command behind the scenes.
Example
This is a terminal session, not the Python shell — the $ is a normal command-line prompt.
terminal
1$ python3 hello.py2Hello, Cynpho3This is my first Python program.
Running the file executes every line top to bottom, exactly once, then returns you to the terminal prompt — unlike the shell, it doesn't wait for more input.
Practice Exercises
Exercise 1 — Save and Run:
Create a file called about_me.py that prints your name and your favourite hobby on two separate lines, then run it.
💡 Two print() calls, saved in a file, then python3 about_me.py in the terminal.
about_me.py
1print("Alex")2print("Rock climbing")
Exercise 2 — Comma Printing:
In one print() call, print the words "Python", "is", and "fun" separated by spaces, using commas.
💡 print() automatically puts a space between every comma-separated item.
python
1print("Python", "is", "fun") # Python is fun
Exercise 3 — Fix the Typo:
This line has a mistake and won't run: print("Hi there" — find the problem and write the corrected version.
💡 Count the parentheses — every ( needs a matching ).
python
1print("Hi there") # the closing ) was missing
Quick Quiz
3 questions · Your First Program
1. What file extension do Python scripts use?
Correct! Python files are saved with a .py extension.
Not quite — Python scripts use the .py extension.
2. Which terminal command runs a file called app.py?
Correct! python3 app.py tells the interpreter to run that file.
Not quite — the interpreter runs a script with python3 app.py.
3. What does print("Python", "is", "fun") output?
Correct! print() joins comma-separated items with a single space between each.
Not quite — comma-separated arguments to print() are joined with a space: Python is fun.
Quiz complete!
Chapter 1
4. Syntax & Comments
Python syntax rules and how to write comments.
Code
python
1# This is a single-line comment2print("Python reads this line") # ignored by Python3"""4This is a multi-line comment.5Python skips this whole block too.6"""7print("...but not this line")
Explanation
Python has no curly braces and no semicolons marking the end of a line. Instead, a statement simply ends at the newline, and indentation — not punctuation — is what groups lines into a block. You'll see this properly once if statements and loops arrive, but the rule starts now: stay consistent, and use 4 spaces per indent level (the PEP 8 convention).
A comment is a note left for humans that Python completely ignores when it runs your code. Start one with # — everything after it on that line is skipped. Comments are perfect for explaining why code does something, not restating what it obviously does.
A """triple-quoted string""" that isn't assigned to anything or returned is created and immediately thrown away, which makes it a handy way to write a multi-line comment. Placed as the very first line inside a function or class, that same syntax becomes a docstring — a real piece of documentation tools can read, not just a discarded note.
How to Use & Where is Used
How to use: Type # before a note for a single line, or wrap several lines in """triple quotes""" for a longer explanation.
Where is used: Explaining tricky logic, documenting functions and classes with docstrings, and leaving notes for teammates (or yourself, months later).
Common in: Every well-maintained open-source project — good docstrings are how tools and other developers understand a function without reading its full implementation.
Example
Indentation only matters inside a block (like a function or an if). Indenting a plain line for no reason is a syntax error — Python will refuse to run it.
python — interactive shell
1>>>print("Line one") # fine — comment after code2Line one3>>>print("Oops")4IndentationError: unexpected indent
The comment on line 1 is harmless — it's just skipped. The unexpected indent on line 3 isn't a comment, and Python has no block there to justify it, so it raises an error instead of guessing what you meant.
Practice Exercises
Exercise 1 — Comment Your Code:
Write one print() statement, and add a single-line comment above it explaining what it does.
💡 Start the comment line with #, on its own line, right above the code it describes.
python
1# Prints a short welcome message2print("Welcome to Python!")
Exercise 2 — Multi-line Note:
Write a triple-quoted comment block describing what a script does, then add one print() call below it.
💡 Triple quotes can span as many lines as you like — Python treats the whole thing as one string.
A variable is a name that refers to a value stored in memory. name = "Maya" creates the variable name and points it at the string "Maya" — the single = is the assignment operator, not a math equals sign.
Python is dynamically typed: you never declare a type up front. A variable's type is simply whatever value it currently holds, and it can hold a different type entirely after a reassignment — age = 25 then later age = "twenty-five" is completely legal.
Naming rules: names can use letters, digits, and underscores, but can't start with a digit and can't be a reserved keyword like class or True. Names are case-sensitive, so age and Age are two different variables. The convention is snake_case for regular variables and UPPER_CASE for values meant to stay constant.
How to Use & Where is Used
How to use: Pick a clear, descriptive name, then assign a value to it with =. Reuse the name later to read or update that value.
Where is used: Absolutely everywhere — user data, configuration values, loop counters, and every intermediate result in a calculation.
Common in: Forms that store a user's name and email, game state like score and lives, and shopping-cart totals.
Example
Reassigning a variable doesn't just change its value — it can change its type too, since Python figures out the type from whatever the variable currently points to.
score starts out as the int 15, then is reassigned to a completely different type — a string. Python doesn't complain; the old value is simply replaced.
Practice Exercises
Exercise 1 — Store Your Info:
Create three variables for your name, age, and favorite language, then print all three on one line.
💡 print() accepts several comma-separated values and prints them on one line.
Exercise 2 — Multiple Assignment:
Assign x, y, and z to 1, 2, and 3 in a single line, then print their sum.
💡 Python lets you write a, b, c = 1, 2, 3 to assign several variables at once.
python
1x, y, z = 1, 2, 32print(x + y + z) # 6
Exercise 3 — Spot the Invalid Name:
Which of these are invalid Python variable names, and why: 2total, total_2, total-2, class?
💡 Check the rule about the first character, the rule about hyphens, and the list of reserved keywords.
notes
1# 2total -> invalid, starts with a digit2# total_2 -> valid3# total-2 -> invalid, "-" is the subtraction operator4# class -> invalid, it's a reserved keyword
Quick Quiz
3 questions · Variables
1. Which symbol assigns a value to a variable in Python?
Correct! A single = is the assignment operator. == is used for comparison instead.
Not quite — a single = assigns a value; == compares two values.
2. Which of these is a valid Python variable name?
Correct! Letters, digits, and underscores are fine as long as the name doesn't start with a digit.
Not quite — variable names can't start with a digit, contain a hyphen, or contain a space.
3. What happens if you reassign a variable to a value of a different type?
Correct! Python is dynamically typed — a variable's type is just whatever it currently points to.
Not quite — reassigning to a different type is completely legal in Python.
Every value in Python has a data type, whether you state it or not. The four you'll use constantly: str for text, int for whole numbers, float for decimal numbers, and bool for True/False. A fifth, None, is its own type — NoneType — and represents "no value at all," not zero or an empty string.
The built-in type() function tells you exactly what type a value is. It's the fastest way to check your assumptions when a variable isn't behaving the way you expect.
Text always needs quotes: "Jit" is a string, but Jit without quotes would be treated as a variable name instead. A whole number like 17 is an int; the moment a decimal point appears, like 99.99, it becomes a float — even if that decimal is .0.
How to Use & Where is Used
How to use: Wrap type(x) around any value or variable to see exactly what data type it is.
Where is used: Databases, APIs, and forms all describe fields by type — Python's built-in types map directly onto that.
Common in: A user profile is a great example — name is a str, age is an int, account_balance is a float, and is_logged_in is a bool.
Example
type() always prints its answer as <class '...'> — the part inside the quotes is the type's actual name.
Correct! type(x) returns exactly what data type x currently is.
Not quite — type() reports a value's data type; it doesn't convert or delete anything.
2. What is the key difference between int and float?
Correct! int is a whole number, float has a decimal point.
Not quite — the difference is whole numbers (int) versus decimal numbers (float).
3. What are the only two possible values of a bool?
Correct! bool values are always exactly True or False, capitalized.
Not quite — a bool is always True or False, not 0/1 or text.
Quiz complete!
Chapter 1
7. Functions in Python
Functions are blocks of reusable code that perform a specific task.
Code
python
1defgreet(name):2 message = f"Hello, {name}!"# f-string — embeds name inside string3return message # sends value back to caller45result = greet("Cynpho") # call the function6print(result) # Hello, Cynpho!
Explanation
We define a function named greet() using the def keyword. It takes one parameter name.
Inside the function, f"Hello, {name}!" is an f-string — the f prefix tells Python to embed the variable value directly into the string.
The return statement sends the value back to wherever the function was called.
On line 5 we call the function by writing its name followed by parentheses: greet("Cynpho"). The result is stored in result and printed.
How to Use & Where is Used
How to use: Define with the def keyword, give it a name, list parameters in parentheses, write the body indented, and call it by name.
Where is used: Functions organize code, avoid repetition, and make programs modular and easy to maintain.
Common in: Web development, data analysis, automation scripts, APIs, and every Python project.
Example
The >>> below is the Python shell prompt — it means the code is typed interactively. Do not type >>> yourself; just type the code after it.
python — interactive shell
1>>>defadd(a, b):2...return a + b3...4>>> total = add(5, 7)5>>>print(total)612
The add() function takes two numbers and returns their sum. Line 6 shows the output — no prompt because it's printed by Python, not typed by you.
Practice Exercises
Exercise 1 — Define and Call:
Write a function called square() that takes one number as a parameter and returns its square. Then call it with the value 9 and print the result.
💡 Use the ** operator to raise a number to a power. 9 ** 2 gives 81.
python
1defsquare(n):2return n **234print(square(9)) # 81
Exercise 2 — Default Parameters:
Write a function called greet() that takes a name and a greeting with a default value of "Hello". Calling greet("Alice") should print Hello, Alice! and greet("Bob", "Hi") should print Hi, Bob!
💡 Default parameters are set in the function signature: def greet(name, greeting="Hello"):
Exercise 3 — Return Multiple Values:
Write a function called min_max() that takes a list of numbers and returns both the smallest and the largest value. Test it with [3, 1, 8, 4, 9, 2].
💡 Python can return multiple values as a tuple: return min(nums), max(nums). You can unpack them with lo, hi = min_max(...).
1. What keyword is used to define a function in Python?
Correct! def is the Python keyword for defining a function.
Not quite — the correct keyword is def. Python uses short, readable keywords throughout.
2. What does the return statement do inside a function?
Correct! return sends a value back to wherever the function was called from.
Not quite — return sends a value back to the caller, it does not print or repeat anything.
3. What will this code print?
def double(n): return n * 2 print(double(6))
Correct! double(6) returns 6 * 2 = 12, which is then printed.
Not quite — double(6) returns 6 * 2, which equals 12.
Quiz complete!
Chapter 1
8. User Input
Reading data from the user with input().
Code
python
1name = input("Enter your name: ")23print("Hello,", name)
Explanation
input() lets a running program pause and wait for a person to type something. Whatever you pass in as an argument — like "Enter your name: " — is displayed first as the prompt, then Python waits until the user presses Enter.
Whatever the user typed is returned as a string, always — even if they typed 42, it comes back as the text "42", not the number. If you need a number, you have to convert it explicitly (the next lesson covers exactly that).
Without input(), every run of a program would behave identically — it's the simplest way to make a program interactive.
How to Use & Where is Used
How to use: Call input("your prompt: ") and store the result in a variable — always write a clear prompt so the user knows what to type.
Where is used: Login and registration flows, command-line calculators and tools, and games, quizzes, and surveys.
Common in: An ATM asking for a PIN, or a quiz app asking the player to type an answer, are both built on exactly this pattern.
Example
In the shell, the prompt text and whatever you type appear on the same line — the text after the prompt below (Alex) is what the user typed.
python — interactive shell
1>>> name = input("Enter your name: ")2Enter your name: Alex3>>>print("Hello,", name)4Hello, Alex
name now holds the string "Alex" — exactly what was typed, nothing more.
Practice Exercises
Exercise 1 — Greet the User:
Ask for the user's name and print a personalized greeting.
💡 Store the result of input() in a variable, then pass it to print().
python
1name = input("What's your name? ")2print("Nice to meet you,", name)
Exercise 2 — Ask Two Things:
Read the user's name and favorite color with two separate input() calls, then print both.
💡 Each call to input() needs its own prompt and its own variable.
Type conversion changes a value from one data type into another. Python does some of this automatically (implicit conversion — like 1 + 2.0 becoming a float without you asking), but most of the time you'll do it yourself with explicit conversion: int(), float(), and str().
age = "17" is a string. int(age) reads that string and returns the whole number 17, which gets reassigned back to age. The same pattern works with float() to turn an int like 10 into 10.0.
The single most common reason you'll reach for this: input() always hands back a string, even when the user typed a number — so any calculation needs an explicit conversion first.
How to Use & Where is Used
How to use: Wrap the value in int(), float(), or str() — always validate untrustworthy data before converting it, so a bad conversion doesn't crash your program.
Where is used: Processing user input, reading data from files and APIs, and any calculation that mixes text with numbers.
Common in: A web form's age field always arrives as text and must be converted to an int before you can do arithmetic with it.
Example
Converting text that isn't actually a valid number raises a ValueError — always a good reason to check the text first.
python — interactive shell
1>>>int("42")2423>>>str(99)4'99'5>>>int("abc")6ValueError: invalid literal for int() with base 10: 'abc'
"abc" isn't a number in any form, so int() has nothing valid to convert and raises an error instead of guessing.
Practice Exercises
Exercise 1 — String to Int:
Convert the string "25" to an int, add 5 to it, and print the result.
💡 "25" + 5 fails — convert the string with int() first.
python
1value = int("25") + 52print(value) # 30
Exercise 2 — Build a Sentence:
Given age = 17, print "Age: 17" by concatenating a string with str(age).
💡 The + operator can't join a string and an int directly — convert the int to a string first.
python
1age = 172print("Age: " + str(age))
Exercise 3 — Fix the Conversion: int("3.5") raises a ValueError. Why, and how would you convert "3.5" into a whole number instead?
💡 int() can't parse a decimal point directly — go through float() first.
1. What's the difference between implicit and explicit type conversion?
Correct! Implicit is automatic (like int + float); explicit means you call int(), float(), or str() yourself.
Not quite — implicit conversion happens automatically; explicit conversion is done manually.
2. What does input() always return, before any conversion?
Correct! input() always returns a string, which is exactly why conversion is so common.
Not quite — input() always returns a string, regardless of what was typed.
3. What exception is raised when a conversion fails, like int("abc")?
Correct! A conversion that can't make sense of the text raises a ValueError.
Not quite — a failed conversion like int("abc") raises a ValueError.
Quiz complete!
Chapter 1
10. Operators
Arithmetic, comparison, and logical operators.
Code
python
1a = 102b = 334print(a + b) # addition5print(a - b) # subtraction6print(a * b) # multiplication7print(a / b) # true division8print(a // b) # floor division9print(a % b) # modulus (remainder)10print(a ** b) # exponent
Explanation
Arithmetic operators combine numbers: +-*///%**. Most are familiar from math class, but two are Python-specific: // is floor division — it divides then drops anything after the decimal point — and % is the modulus, which returns only the remainder of a division.
Comparison operators — ==!=><>=<= — compare two values and always produce a bool: True or False. Don't confuse == (compares) with = (assigns) — mixing them up is one of the most common beginner mistakes.
Logical operators — and, or, not — combine or invert boolean values. and needs both sides to be true, or needs at least one side true, and not flips True to False and back.
How to Use & Where is Used
How to use: Place an operator between two values to build an expression — Python evaluates ** first, then * / // %, then + -, similar to the math order of operations.
Where is used: Calculating totals and averages, comparing scores or ages, and combining multiple conditions in decision-making code.
Common in: A shopping cart totaling item prices, or a game checking whether a score beats a high score.
Example
Wrap comparisons in parentheses when combining them with and/or — it makes the intent obvious at a glance.
Correct! // is floor division — it keeps only the whole-number part of the result.
Not quite — / is true division, // is floor division (it drops anything after the decimal point).
2. What does the modulus operator % compute?
Correct! % returns whatever is left over after floor division — commonly used to check even/odd numbers.
Not quite — % is the modulus: the remainder after division.
3. What does and require in order to return True?
Correct! and only returns True when both operands are true — that's the whole point of "and."
Not quite — and needs both sides to be true; for "at least one," use or instead.
Quiz complete!
Chapter 2
11. If Statements
Running code only when a condition is true.
Code
python
1age = 1823if age >= 18:4print("Adult")
Explanation
The if statement lets a program execute a block of code only when a specific condition evaluates to True. If the condition is False, Python simply skips the indented block beneath it.
In the example above, age >= 18 compares two values and produces a boolean result. Since 18 >= 18 is True, Python runs the indented line and prints "Adult".
Indentation — not braces or parentheses — is what defines an if block in Python. Every line that belongs to the block must be indented by the same amount, conventionally four spaces.
How to Use & Where is Used
How to use: Write if, then a condition, then a colon — everything indented on the following lines only runs when that condition is True.
Where is used: Login systems and security checks, games and AI decision-making, and validating user input before it's processed.
Common in: Age verification before granting access to age-restricted content, or checking a password before allowing a login to proceed.
Example
Read a condition like a plain English sentence — "if age is greater than or equal to 18" — it makes logic mistakes much easier to spot.
python — interactive shell
1>>> age = 152>>>if age >= 18:3...print("Adult")4...
Because 15 >= 18 is False, the indented print() line never runs — the program produces no output at all for this block.
Practice Exercises
Exercise 1 — Temperature Check:
Given temperature = 35, print "Hot day!" if the temperature is above 30.
💡 Compare temperature to 30 using >, then put print() on the indented line beneath the if.
python
1temperature = 3523if temperature > 30:4print("Hot day!")
Exercise 2 — Login Check:
Given password = "cynpho123", print "Access granted" if it matches "cynpho123".
💡 Use == to compare two strings for equality — not =, which assigns instead of compares.
Exercise 3 — Free Shipping:
Given cart_total = 55, print "You get free shipping!" if the total is at least $50.
💡 "At least" means the condition should also be True when the values are exactly equal — use >=.
python
1cart_total = 5523if cart_total >= 50:4print("You get free shipping!")
Quick Quiz
3 questions · If Statements
1. What data type must the condition in an if statement evaluate to?
Correct! An if condition always evaluates to a boolean — True runs the block, False skips it.
Not quite — an if condition always reduces to a boolean, True or False, no matter how complex the comparison looks.
2. What is the difference between = and == in Python?
Correct! = stores a value in a variable; == checks whether two values are equal.
Not quite — = is assignment, == is comparison. Mixing them up is one of the most common beginner mistakes.
3. What happens if you forget the colon after an if condition?
Correct! The colon is required — leaving it out immediately raises a SyntaxError before the code even runs.
Not quite — Python requires the colon after an if condition; without it, you'll get a SyntaxError.
Quiz complete!
Chapter 2
12. If…Else
Choosing between two code paths.
Code
python
1age = 1523if age >= 18:4print("Adult")5else:6print("Minor")
Explanation
if-else provides two possible paths of execution: one branch runs when the condition is True, the other runs when it's False — exactly one of the two always executes.
Here, age >= 18 is False because 15 is less than 18, so Python skips the if block and instead runs the else block, printing "Minor".
Unlike a plain if with no else, this guarantees the program always does something meaningful for both outcomes — the positive case and the negative one.
How to Use & Where is Used
How to use: Add an else: block directly beneath an if block's indented code — it runs only when the if condition was False.
Where is used: Login success/failure handling, payment approval or decline, and pass/fail results in grading systems.
Common in: A payment system that either approves a transaction or declines it — there's always exactly one outcome, never both and never neither.
Example
Always ask "what happens when this condition is False?" — it's easy to design only for the success path and forget the else.
python — interactive shell
1>>> age = 202>>>if age >= 18:3...print("Adult")4...else:5...print("Minor")6...7Adult
This time age >= 18 is True, so only the if branch runs — the else block is skipped entirely.
Practice Exercises
Exercise 1 — Even or Odd:
Given number = 7, print "Even" if it's even, otherwise print "Odd".
💡 A number is even when number % 2 == 0 — the modulus tells you the remainder after dividing by 2.
python
1number = 723if number % 2 == 0:4print("Even")5else:6print("Odd")
Exercise 2 — Pass or Fail:
Given marks = 35, print "Pass" if marks are 40 or above, otherwise print "Fail".
💡 Use >= for "at least this value," and put the failing case in the else block.
python
1marks = 3523if marks >= 40:4print("Pass")5else:6print("Fail")
Exercise 3 — Ticket Price:
Given age = 10, print "Child ticket" if age is under 12, otherwise print "Adult ticket".
💡 "Under 12" means strictly less than 12 — use <.
python
1age = 1023if age < 12:4print("Child ticket")5else:6print("Adult ticket")
Quick Quiz
3 questions · If…Else
1. Can both the if block and the else block run in the same execution?
Correct! Exactly one branch of an if-else always runs — they're mutually exclusive.
Not quite — if-else guarantees exactly one branch runs, never both and never neither.
2. When does the else block execute?
Correct! The else block is the fallback — it only runs when the if condition evaluates to False.
Not quite — else only runs when the if condition was False.
3. Why is if-else important for real-world applications?
Correct! Most real decisions have two outcomes worth handling — if-else lets your program respond to both.
Not quite — the real value of if-else is handling both possible outcomes of a check, not just the success case.
Quiz complete!
Chapter 2
13. Elif Ladder
Handling multiple conditions in sequence.
Code
python
1marks = 8223if marks >= 90:4print("Grade A")5elif marks >= 80:6print("Grade B")7elif marks >= 70:8print("Grade C")9else:10print("Fail")
Explanation
elif is short for "else if" — it lets a program check several conditions in sequence, one after another, extending if/else to handle more than two possible outcomes.
Python checks each condition top to bottom and stops at the first one that's True — here, 82 >= 90 is False, but 82 >= 80 is True, so "Grade B" prints and every condition below it is skipped.
A final else catches whatever doesn't match any condition above it — order matters: conditions should generally run from most specific to most general.
How to Use & Where is Used
How to use: Stack one or more elif condition: blocks between an if and an optional final else — Python runs the first True branch and skips the rest.
Where is used: Grading systems with multiple grade bands, menu systems with more than two options, and pricing rules with several tiers.
Common in: A grading system that maps a numeric score to a letter grade is one of the most common real-world uses of elif.
Example
Arrange conditions from most specific to most general — otherwise a narrower case can get caught by a broader one that comes first.
python — interactive shell
1>>> marks = 952>>>if marks >= 90:3...print("Grade A")4...elif marks >= 80:5...print("Grade B")6...else:7...print("Grade C or below")8...9Grade A
Because 95 >= 90 is already True, Python runs the first matching branch and never even checks the elif or else below it.
Practice Exercises
Exercise 1 — Traffic Light:
Given light = "yellow", print "Go", "Slow down", or "Stop" for "green", "yellow", "red" respectively.
💡 Chain the checks with elif — compare light to each string with ==.
1. Does Python check every elif block, or stop at the first True one?
Correct! Python stops at the first True condition in the chain — everything after it is skipped, even if it would also be True.
Not quite — Python stops checking as soon as one condition is True; the rest of the chain never runs.
2. How many elif blocks can a single if chain have?
Correct! There's no built-in limit — you can chain as many elif blocks as the logic requires.
Not quite — Python places no limit on how many elif blocks a single chain can have.
3. Why does condition ORDER matter in an if/elif chain?
Correct! Since Python stops at the first True match, a narrower condition needs to come before a broader one that would otherwise catch it first.
Not quite — order affects correctness, not just style: a specific case listed after a broader one may never be reached.
Quiz complete!
Chapter 2
14. Nested If
Conditions inside conditions.
Code
python
1age = 202has_id = True34if age >= 18:5if has_id:6print("Access Granted")
Explanation
A nested if is an if statement placed inside another if statement — used when one decision genuinely depends on a previous one having already been satisfied.
Here, the inner if has_id: is only even checked because the outer condition age >= 18 was already True. Both conditions must pass for "Access Granted" to print.
Nesting expresses a genuine dependency between decisions — but keep it shallow. Two levels is usually the practical limit before the logic becomes hard to follow.
How to Use & Where is Used
How to use: Indent a second if inside the first one's block — the inner condition is only evaluated when the outer one is already True.
Where is used: Banking systems with multiple verification steps, two-factor authentication, and AI decision trees.
Common in: ATM verification often nests conditions: first check the card is valid, then — only if that passed — check the PIN is correct.
Example
If nesting starts to feel hard to read, that's a signal to split the logic into a well-named helper function instead of nesting further.
python — interactive shell
1>>> age = 162>>> has_id = True3>>>if age >= 18:4...if has_id:5...print("Access Granted")6...
The outer condition age >= 18 is already False here, so the inner if has_id: is never even reached — nothing prints.
Practice Exercises
Exercise 1 — Two-Factor Check:
Given password_ok = True and otp_ok = False, print "Login successful" only if both are True.
💡 Nest a second if inside the first — or combine both with and, which reads just as clearly here.
Exercise 2 — Movie Rating:
Given age = 15 and has_parent = False, print "Can watch" only if age is 18+, or if age is at least 13 and a parent is present.
💡 This needs an outer check for the age-13 case, with a nested check for parental presence inside it.
python
1age = 152has_parent = False34if age >= 18:5print("Can watch")6elif age >= 13:7if has_parent:8print("Can watch")
Exercise 3 — Loan Approval:
Given income = 45000 and credit_score = 720, print "Loan approved" only if income is at least 30000 and credit_score is at least 700.
💡 Check income first — only look at the credit score if that outer condition already passed.
Correct! A nested if is simply an if statement written inside the block of another if statement.
Not quite — nesting specifically means placing one if statement inside another one's indented block.
2. When is nesting genuinely necessary instead of just using and?
Correct! Nesting is most useful when each level needs to respond differently — like showing a specific message for each failed step.
Not quite — plain and often works fine for a single combined check; nesting earns its place when each condition needs distinct handling.
3. How can excessive nesting be avoided in real code?
Correct! Once nesting gets deep, pulling the logic out into its own function usually reads far more clearly.
Not quite — the standard fix for deep nesting is refactoring it into a separate function.
Quiz complete!
Chapter 2
15. For Loops
Iterating over sequences.
Code
python
1for i inrange(1, 6):2print(i)
Explanation
A for loop iterates over a sequence of items, running its indented block once for each one — it automates repetition that would otherwise mean writing the same code by hand for every item.
range(1, 6) produces the numbers 1, 2, 3, 4, 5 — the stop value, 6, is never included. Each pass through the loop, i takes the next value in that sequence.
A for loop can iterate over far more than range() — lists, tuples, strings, dictionaries, and sets are all iterable in exactly the same way.
How to Use & Where is Used
How to use: Write for, a loop variable name, in, then an iterable, followed by a colon — the indented block runs once per item.
Where is used: Iterating over lists, tuples, strings, dictionaries, and sets; processing files line by line; and general automation or data-processing tasks.
Common in: Printing every student's name in a class list, or processing every row returned from a database query, are both classic for-loop use cases.
Example
Prefer a for loop whenever the number of iterations — or the collection being processed — is already known in advance.
python — interactive shell
1>>>for i inrange(3):2...print(i * i)3...405164
range(3) produces 0, 1, 2 — for each one, the loop prints its square, giving 0, 1, and 4.
Practice Exercises
Exercise 1 — Countdown:
Print the numbers 5 down to 1 (inclusive), one per line.
💡 range() can count downward too — pass a negative step as its third argument.
python
1for i inrange(5, 0, -1):2print(i)
Exercise 2 — Sum of a List:
Given numbers = [4, 8, 15, 16, 23], print the total of all the numbers.
💡 Start a total = 0 variable before the loop, then add each number to it as you iterate.
python
1numbers = [4, 8, 15, 16, 23]2total = 034for n in numbers:5 total = total + n67print(total)
Exercise 3 — Print Even Numbers:
Print every even number from 1 to 10 using a for loop.
💡 Loop through range(1, 11) and use if n % 2 == 0 to filter for even numbers.
python
1for n inrange(1, 11):2if n % 2 == 0:3print(n)
Quick Quiz
3 questions · For Loops
1. What values does range(1, 6) actually produce?
Correct! range(1, 6) starts at 1 and stops before 6, producing 1 through 5.
Not quite — range()'s stop value is never included, so range(1, 6) produces 1, 2, 3, 4, 5.
2. What is an "iterable" in Python?
Correct! An iterable is anything a for loop can step through one item at a time — lists, tuples, strings, dictionaries, sets, and range() all qualify.
Not quite — an iterable is any object a for loop can step through item by item, not just range().
3. When would you prefer a for loop over a while loop?
Correct! A for loop is the natural fit when the iteration count or collection is already known — a while loop suits condition-based repetition instead.
Not quite — a for loop shines when the number of repetitions (or the collection itself) is already known ahead of time.
A while loop repeats its block for as long as a given condition remains True, checking that condition before every single pass.
Here, count <= 5 is checked before each iteration; as long as it holds, Python prints count and then increments it with count += 1.
That increment step is essential — without something inside the loop that eventually makes the condition False, a while loop runs forever.
How to Use & Where is Used
How to use: Write while, then a condition, then a colon — the indented block repeats as long as that condition stays True, so make sure something inside it moves toward ending the loop.
Where is used: Situations where the number of iterations isn't known in advance, menus that repeat until the user chooses to quit, and polling or retry logic that waits for a condition to change.
Common in: A command-line menu that keeps showing options and re-prompting until the user specifically chooses "Quit" is a classic while-loop pattern.
Example
If you're unsure whether a loop will ever end, trace through the condition by hand with the loop's actual starting values before running it.
Exercise 3 — Sum Until Limit:
Starting from 1, keep adding numbers to a total until the total reaches or exceeds 20, then print the total.
💡 The loop's condition should check the running total, not a fixed counter.
python
1total = 02n = 134while total < 20:5 total += n6 n += 178print(total)
Quick Quiz
3 questions · While Loops
1. When is the while loop's condition checked — before or after each pass?
Correct! A while loop checks its condition before every single pass — if it's False from the start, the loop body never runs at all.
Not quite — the condition is checked before each pass, not after.
2. What would happen if count += 1 were removed from this loop?
Correct! Without something that eventually changes the condition, count would stay at 1 forever and the loop would never end.
Not quite — removing that line means count never changes, so the condition stays True forever and the loop never ends.
3. When would you choose a while loop over a for loop?
Correct! A while loop fits naturally when repetition depends on a condition rather than a known count or collection.
Not quite — while loops are the better fit when you don't know the number of iterations in advance.
Quiz complete!
Chapter 2
17. Break, Continue, Pass
Controlling loop execution.
Code
python
1for i inrange(1, 11):23if i == 5:4continue56if i == 9:7break89print(i)
Explanation
break and continue are loop-control statements that change a loop's normal flow: break exits the loop immediately, continue skips straight to the next iteration without running the rest of the current one.
In the example above, continue skips printing 5 and jumps straight to the next value of i, while break stops the loop completely the moment i reaches 9 — so 9 and 10 never print either.
pass is a third, unrelated keyword that does absolutely nothing — it's a placeholder used where Python's syntax requires a statement but you don't have one yet, like an empty function body or an unfinished branch of an if statement.
How to Use & Where is Used
How to use: Place continue to skip just the current pass, or break to exit the loop entirely — pass goes anywhere a statement is syntactically required but no action is needed yet.
Where is used: Searching, where you stop as soon as a match is found; skipping invalid records while processing data; and stubbing out functions or classes you haven't written yet.
Common in: Searching a list for the first matching item and breaking immediately once found avoids wasting time scanning the rest of a potentially huge list.
Example
break is especially valuable in search-style loops — once the answer is found, there's no reason to keep looping.
python — interactive shell
1>>> names = ["Alice", "Bob", "Cynthia"]2>>>for name in names:3...if name == "Bob":4...print("Found Bob!")5...break6...7Found Bob!
As soon as "Bob" is found, break exits the loop immediately — the remaining name, "Cynthia", is never even checked.
Practice Exercises
Exercise 1 — Skip Multiples of 3:
Print numbers 1 through 10, but skip any multiple of 3.
💡 Use continue inside an if number % 3 == 0: check to skip that pass.
python
1for number inrange(1, 11):2if number % 3 == 0:3continue4print(number)
Exercise 2 — Stop at First Negative:
Given numbers = [4, 9, -2, 6, 8], print each number until you reach the first negative one, then stop.
💡 Check each number with if number < 0: and break as soon as it's true.
python
1numbers = [4, 9, -2, 6, 8]23for number in numbers:4if number < 0:5break6print(number)
Exercise 3 — Placeholder Function:
Write a function named coming_soon that takes no arguments and does nothing yet — its body should just be pass.
💡 pass is exactly what belongs inside a function body you plan to finish writing later.
python
1defcoming_soon():2pass
Quick Quiz
3 questions · Break, Continue, Pass
1. What does break do inside a loop?
Correct! break exits the loop immediately — nothing after it inside that loop runs again.
Not quite — break stops the loop completely; that's continue's job to skip just one pass.
2. What does continue do inside a loop?
Correct! continue jumps straight to the next iteration, skipping whatever code comes after it in the current pass.
Not quite — continue skips only the rest of the current pass; it doesn't exit the loop like break does.
3. What is the pass statement used for?
Correct! pass is a no-op — it lets you write syntactically valid, empty function bodies, classes, or branches while you're still working on them.
Not quite — pass does nothing at all; it's just a placeholder for code you haven't written yet.
A list is an ordered, mutable collection that can store multiple values — including duplicates and mixed types — in a single variable, written with square brackets.
fruits[0] accesses the first item using its index — Python counts from 0, so fruits[0] is "Apple", not "Banana".
fruits.append("Mango") adds a new item to the end of the list in place — unlike strings, lists can be modified freely after they're created.
How to Use & Where is Used
How to use: Wrap comma-separated values in square brackets to create a list, then access or modify items using their index — list[0] is the first item, list[-1] is the last.
Where is used: Student records and shopping carts, game inventories, and data analysis or machine learning pipelines.
Common in: A shopping cart holding item names, or a task manager holding a growing list of to-do items, are both natural fits for a Python list.
Example
Lists are the most commonly used collection type in Python — comfort with them pays off across almost every project you write.
len(fruits) returns the number of items in the list, and fruits[-1] uses a negative index to grab the last item without needing to know the list's length.
Practice Exercises
Exercise 1 — Build a List:
Create a list called colors containing "Red", "Green", and "Blue", then print it.
💡 Wrap the three strings in square brackets, separated by commas.
python
1colors = ["Red", "Green", "Blue"]2print(colors)
Exercise 2 — Add and Access:
Given numbers = [10, 20, 30], append 40 to the list, then print the last item.
💡 Use .append() to add to the end, then numbers[-1] to grab the last item.
A tuple is an ordered, immutable collection of values — once created, its contents can never be changed, added to, or removed, and it's written with parentheses instead of square brackets.
colors[1] accesses the item at index 1 — counting from 0, that's "Green", the second item in the tuple.
len(colors) returns 3, the total number of items — the same len() function works on lists, strings, and tuples alike.
How to Use & Where is Used
How to use: Wrap comma-separated values in parentheses to create a tuple — you can read items by index just like a list, but you can never modify, add, or remove them afterward.
Where is used: Coordinates and fixed configuration values, database records and function return values, and any data that must not change after creation.
Common in: GPS coordinates (latitude, longitude) or an RGB color value are natural tuples — their individual values are always meant to travel together and never change independently.
Example
Use a tuple any time you want to signal, just through the data type itself, that a value should be treated as fixed.
python — interactive shell
1>>> point = (4, 7)2>>> point[0]344>>> point[0] = 105Traceback (most recent call last):6TypeError: 'tuple' object does not support item assignment
Trying to change a tuple's contents raises a TypeError — this immutability is the whole point of choosing a tuple over a list.
Practice Exercises
Exercise 1 — Build a Tuple:
Create a tuple called weekend containing "Saturday" and "Sunday", then print it.
💡 Wrap the two strings in parentheses, separated by a comma.
python
1weekend = ("Saturday", "Sunday")2print(weekend)
Exercise 2 — Access by Index:
Given dimensions = (1920, 1080), print the width (first item) and height (second item) on separate lines.
A set is an unordered collection of unique values, written with curly braces — Python automatically discards duplicates and gives no guaranteed order.
numbers.add(6) inserts a new value. numbers.add(3) does nothing visible — 3 is already in the set, and a set can never contain the same value twice.
numbers.remove(2) deletes 2 from the set. Because sets are unordered, don't expect print(numbers) to show items in the order you added them.
How to Use & Where is Used
How to use: Wrap comma-separated values in curly braces to create a set, then use .add() and .remove() to change it — never index it with set[0].
Where is used: Removing duplicates from a collection, fast membership testing, and mathematical set operations like union, intersection, and difference.
Common in: Removing duplicate tags from a list of user-submitted tags, or quickly checking whether a username already exists in a large collection.
Example
set(some_list) is the fastest, most idiomatic way to de-duplicate a list in Python.
python — interactive shell
1>>> numbers = {1, 2, 3}2>>> numbers[0]3Traceback (most recent call last):4TypeError: 'set' object is not subscriptable
Sets have no order and no index, so numbers[0] raises a TypeError — use the in operator (e.g. 1 in numbers) to check membership instead.
Practice Exercises
Exercise 1 — Build a Set:
Create a set called pets containing "Dog", "Cat", and "Dog" again, then print it.
💡 Sets automatically drop duplicate values, no matter how many times you add them.
python
1pets = {"Dog", "Cat", "Dog"}2print(pets)
Exercise 2 — Membership Test:
Given allowed = {"admin", "editor", "viewer"}, check whether "guest" is in allowed and print the result.
A dictionary is a collection of key-value pairs — instead of a numeric index, you look up a value using the key it's stored under, inside curly braces with a colon between each key and value.
student["name"] looks up the value stored under the key "name", which is "Jit".
student["age"] = 18 updates the existing "age" key in place — dictionaries are mutable, just like lists.
How to Use & Where is Used
How to use: Create with {key: value, ...}, then read or update a value with dict[key] — use .get(key, default) when the key might not exist.
Where is used: Representing structured records (a user profile, a product), configuration settings, and fast lookups by name or ID.
Common in: A user profile with fields like name, email, and age is a textbook dictionary — the same shape as a JSON object.
Example
student.get("email", "Not Provided") safely returns a fallback value instead of crashing when a key is missing.
greeting="Hello" gives that parameter a default value — if the caller doesn't supply one, Python uses "Hello" automatically, which is exactly what happens on line 4.
On line 5, passing a second value — "Welcome" — overrides the default, so greeting becomes "Welcome" for that call only.
A function can also hand back more than one value at once: return a, b actually packages them into a single tuple, which you can unpack directly, e.g. x, y = get_coordinates().
Beyond a fixed default, Python has two special parameter forms for when you don't know in advance how many arguments will arrive: *args and **kwargs — covered in the example below.
How to Use & Where is Used
How to use: Give a parameter a default with name=value in the signature to make it optional, or prefix a parameter with * or ** to collect an unknown number of extra arguments.
Where is used: Functions where most calls use the same value but occasionally need to override it — logging levels, retry counts, request timeouts, and flexible configuration options.
Common in: A connect(timeout=30) style function that almost always uses its default, but lets advanced callers override it when they need to.
Example
*args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dictionary.
*args gathers any number of extra positional arguments into a tuple, so total() works no matter how many numbers are passed in. The double-star version, **kwargs, works the same way for keyword arguments — it collects any number of name=value pairs into a dictionary inside the function, which is how many real Python libraries accept flexible configuration options.
Practice Exercises
Exercise 1 — Default Greeting:
Write a function greet(name, punctuation="!") that returns name followed by punctuation. Call it once with just a name, and once overriding the punctuation with "?".
💡 Give punctuation a default value directly in the function signature.
python
1defgreet(name, punctuation="!"):2return name + punctuation34print(greet("Jit"))5print(greet("Jit", "?"))
Exercise 2 — Sum Any Numbers:
Write a function total(*args) that returns the sum of however many numbers are passed in. Call it with 2 numbers, then with 5.
💡 *args collects every positional argument into a tuple you can pass straight to sum().
Exercise 3 — Flexible Config:
Write a function show_config(**kwargs) that prints each keyword argument it receives on its own line, as key: value. Call it with mode="dark" and retries=3.
💡 **kwargs is a dictionary inside the function — loop over it with .items().
python
1defshow_config(**kwargs):2for key, value in kwargs.items():3print(key + ":", value)45show_config(mode="dark", retries=3)
Quick Quiz
3 questions · Functions
1. What does giving a parameter a default value, like def greet(name, greeting="Hello"), actually do?
Correct! A default value makes that parameter optional — the caller can leave it out and Python fills it in automatically.
Not quite — a default value makes the parameter optional, not required.
2. What does *args collect inside a function?
Correct! *args gathers any number of extra positional arguments into a tuple.
Not quite — *args collects any number of positional arguments into a tuple.
3. What does **kwargs collect inside a function?
Correct! **kwargs gathers any number of keyword arguments into a dictionary.
Not quite — **kwargs collects keyword arguments into a dictionary inside the function.
Quiz complete!
Chapter 4
23. Parameters & Args
Positional and keyword arguments, and the difference between a parameter and an argument.
def greet(name, age): defines two parameters — named placeholders that only exist inside the function's own definition.
greet("Jit", 17) passes two argumentspositionally: "Jit" binds to name and 17 binds to age, purely because of the order they're written in.
A parameter is the name used in the definition; an argument is the actual value supplied at the call site — the two terms describe the same slot at two different moments.
How to Use & Where is Used
How to use: List parameter names in the function's parentheses, then pass matching values by position, or by name using keyword=value, when you call it.
Where is used: Any function that needs to operate on caller-provided data — practically every real function you will ever write.
Common in: A create_user(username, email, password) function needs different arguments every single time it's called, once per new user.
Example
greet(age=17, name="Jit") works identically to positional order, because keyword arguments are matched by name, not position.
Because the arguments are matched by name instead of position, greet(age=17, name="Jit") gives the exact same result as greet("Jit", 17) — keyword arguments free you from having to remember the exact parameter order.
Practice Exercises
Exercise 1 — Positional Call:
Write a function describe(item, price) that prints item and price on separate labeled lines, then call it positionally with "Book" and 12.
💡 The first value you pass binds to the first parameter, purely by order.
Exercise 2 — Keyword Call:
Call the same describe(item, price) function from Exercise 1, but this time pass price before item, using keyword arguments.
💡 Use price=..., item=... — keyword arguments don't care about order.
python
1describe(price=12, item="Book")
Exercise 3 — Spot the Bug:
For def greet(name, age):, the call greet(17, "Jit") runs without crashing, but prints the wrong thing. Rewrite the call so "Jit" correctly binds to name and 17 to age.
💡 Either swap the order back, or use keyword arguments to be explicit.
key=lambda p: p["age"] tells sorted() to compare people by their "age" value — no separate named function required just to describe the sort order.
Practice Exercises
Exercise 1 — Basic Lambda:
Write a lambda called double that returns twice its input, then call it with 9.
💡 lambda x: x * 2
python
1double = lambda x: x * 22print(double(9))
Exercise 2 — Filter with Lambda:
Given nums = [1, 2, 3, 4, 5, 6], use filter() with a lambda to keep only the even numbers, then print the result as a list.
💡 x % 2 == 0 checks evenness; wrap filter() in list() to see the results.
Correct! It's a small, anonymous function — no name, no def, just a single expression.
Not quite — lambda x: x * x creates a small anonymous function that returns x * x.
2. Which keyword is required inside a lambda's body to send back a value?
Correct! A lambda has no return statement — its single expression's value is returned automatically.
Not quite — a lambda never uses return; the expression's result is returned automatically.
3. Lambdas are most commonly used for...
Correct! Lambdas shine as short, inline functions passed straight into another function.
Not quite — lambdas are best for short, throwaway logic passed directly into functions like sorted() or map().
Quiz complete!
Chapter 4
25. Modules
Organising code across multiple files.
Code
python
1# math_utils.py2defadd(a, b):3return a + b45# main.py6import math_utils78print(math_utils.add(2, 3))
Explanation
A module is simply a .py file containing Python code — functions, variables, classes — that can be reused from other files.
import math_utils loads math_utils.py as a module object; math_utils.add(2, 3) reaches the function through the module's name.
Python runs the imported file once, top to bottom, the first time it's imported — after that, its functions and variables are simply available through the module name.
How to Use & Where is Used
How to use: Save reusable code in its own .py file, then write import filename (no .py extension) in another file in the same folder to reach everything inside it via filename.something.
Where is used: Splitting a large program into logical, maintainable pieces — one file per responsibility — is a core habit in every real-world project.
Common in: A project with separate files like database.py, utils.py, and main.py, each handling one concern.
Example
from module import name imports just one name directly, so you can call it without the module prefix.
python — interactive shell
1>>>from math_utils import add2>>> add(2, 3)35
from math_utils import add pulls just that one function into the current file, so it's called directly as add(2, 3) instead of math_utils.add(2, 3).
Practice Exercises
Exercise 1 — Import a Module:
A file string_utils.py contains def shout(text): return text.upper() + "!". Write the import line and a call that prints shout("hello").
💡 import string_utils, then reach the function through string_utils.shout(...).
Exercise 3 — Alias an Import:
Import the built-in math module under the shorter alias m, then print the square root of 81.
💡 import math as m, then m.sqrt(...).
python
1import math as m2print(m.sqrt(81))
Quick Quiz
3 questions · Modules
1. What is a Python module?
Correct! A module is just a .py file whose code you can reuse from other files.
Not quite — a module is a .py file containing reusable code.
2. What does from math_utils import add let you do differently from import math_utils?
Correct! Importing a specific name lets you call it directly, without the module prefix.
Not quite — from module import name lets you call that name directly, with no prefix.
3. What does import math as m do?
Correct! as m gives the imported module a shorter local alias, m.
Not quite — import math as m just gives the module a shorter alias to use locally.
Quiz complete!
Chapter 4
26. Import Statement
Loading modules and packages.
Code
python
1import math2from math import pi3from math import sqrt as square_root45print(math.pi)6print(pi)7print(square_root(16))
Explanation
import math loads the whole module — every name inside it is reached through math.name.
from math import pi pulls just that one name straight into the current file, so a bare pi works with no prefix.
from math import sqrt as square_root combines both ideas: it imports one name and gives it a new local name in a single line.
How to Use & Where is Used
How to use: Choose import module, from module import name, or from module import name as alias — whichever keeps the call sites clearest.
Where is used: The Python Standard Library ships dozens of ready-made modules — math, random, datetime, os — and pip lets you install thousands more written by other people.
Common in:import random for generating random values, or from datetime import datetime for working with dates and times.
Example
Avoid from module import * in real projects — it pulls in every public name at once, making it unclear where a name came from.
python — interactive shell
1>>>from math import *2>>> sqrt(25)35.04>>> pi53.141592653589793
from math import * pulls in every public name from math at once — convenient here, but in a larger file it can silently overwrite names you've already defined, which is why most Python style guides recommend avoiding it.
Practice Exercises
Exercise 1 — Import the Whole Module:
Import the random module and use random.randint(1, 10) to print a random number between 1 and 10.
💡 import random, then random.randint(low, high).
python
1import random2print(random.randint(1, 10))
Exercise 2 — Import One Name:
Import just the randint function from random directly, then call it the same way, without the module prefix.
💡 from random import randint
python
1from random import randint2print(randint(1, 10))
Exercise 3 — Import with an Alias:
Import the datetime class from the datetime module under the alias dt, then print the current date and time using dt.now().
💡 from datetime import datetime as dt
python
1from datetime import datetime as dt2print(dt.now())
Quick Quiz
3 questions · Import Statement
1. What's the main risk of using from module import *?
Correct! Star imports make it unclear where any given name came from, and can silently overwrite existing ones.
Not quite — the real risk is unclear name origins and silent overwrites, not speed or legality.
2. What does from math import sqrt as square_root do?
Correct! It imports just sqrt and gives it a new local name, square_root.
Not quite — it imports only sqrt, under the local alias square_root.
3. Where do third-party Python packages (not part of the Standard Library) typically come from?
Correct! pip is the standard tool for installing third-party packages written by the wider Python community.
Not quite — third-party packages are typically installed separately with pip.
A class is a blueprint for creating objects; an object is one specific instance of that blueprint, holding its own data.
__init__ is the constructor — it runs automatically the moment Player("Jit", 100) executes, and self refers to this particular object being built.
self.name = name stores the passed-in value directly onto that object, so player.show_info() can read it back afterward.
How to Use & Where is Used
How to use: Define a class with class Name:, add __init__(self, ...) to set up its data, then create real objects by calling the class like a function, e.g. Player("Jit", 100).
Where is used: Modeling real-world entities that combine data and behavior together — a Player, an Order, a Customer.
Common in: An e-commerce system modeling Product, Customer, and Order as classes, each combining related data and behavior in one place.
Example
Every attribute set with self.xxx = ... inside __init__ becomes part of that specific object — different objects can hold completely different values at the same time.
Exercise 3 — Two Independent Objects:
Create two separate Book objects with different titles and authors, and print both titles to show they don't interfere with each other.
💡 Each object created from the class keeps its own separate copy of the attributes.
__init__ is a special method — the constructor — that Python runs automatically the instant Car("Toyota", "Supra") executes, before anything else touches the object.
self.brand = brand and self.model = model store the two passed-in values onto the new object; car.show() simply reads them back afterward.
Because the constructor runs automatically, an object can never exist in a half-set-up state — it starts life already holding exactly the values you require.
How to Use & Where is Used
How to use: Define def __init__(self, ...): inside a class to require and store any data the object genuinely needs from the moment it's created.
Where is used: Any class needing guaranteed initial values — nearly every class you will write.
Common in: A BankAccount class whose constructor requires an owner name and starting balance, making it impossible to create an account with missing required information.
Example
Constructor parameters can have default values too, just like any other function — making some arguments optional at creation time.
Giving model a default value means Car("Honda") still works even without one — it simply falls back to "Unknown", exactly like a default value on any other function.
Practice Exercises
Exercise 1 — Basic Constructor:
Define a class Laptop whose constructor takes brand and stores it on self, then create one and print its brand.
Exercise 3 — Default Value:
Give ram_gb a default of 8 in the constructor from Exercise 2, then create a Laptop passing only a brand, and print its ram_gb.
class Dog(Animal): — the parentheses mean Dog inherits from Animal, automatically reusing everything Animal defines.
dog.speak() works even though speak() is only physically written inside Animal, because Dog gains everything its parent has.
dog.bark() calls the method defined directly on Dog itself — a subclass can freely add its own methods on top of what it inherits.
How to Use & Where is Used
How to use: Write class Child(Parent): so Child automatically gains everything Parent defines, then add or override methods directly on Child.
Where is used: Modeling genuine "is-a" relationships (a Dog IS AN Animal) and sharing common behavior across a family of related classes.
Common in: A UI framework where Button, TextBox, and Checkbox all inherit shared positioning and rendering logic from a common Widget base class.
Example
Python supports multiple inheritance too — class Dog(Animal, Pet): — unlike languages such as Java or C#.
python — interactive shell
1>>>class Pet:2...defis_friendly(self):3...returnTrue4...5>>>class Dog(Animal, Pet):6...defbark(self):7...print("Woof!")8...9>>> dog = Dog()10>>> dog.is_friendly()11True
class Dog(Animal, Pet) inherits from both classes at once — Dog gets speak() from Animal and is_friendly() from Pet simultaneously. This is called multiple inheritance.
Practice Exercises
Exercise 1 — Basic Inheritance:
Define a class Vehicle with a method move() that prints "Moving...", then a class Car(Vehicle) with its own method honk() that prints "Beep!". Create a Car and call both methods.
💡 class Car(Vehicle): — the parentheses do the inheriting.
Exercise 2 — Multiple Subclasses:
Define a second class Bike(Vehicle) (reusing Vehicle from Exercise 1) with its own method ring_bell() that prints "Ring ring!". Create a Bike and call move() and ring_bell().
💡 Bike inherits move() from Vehicle the same way Car did.
Exercise 3 — Spot the Relationship:
A Cat genuinely "is-a" kind of Animal. Write that relationship as actual code, giving Animal a speak() method and Cat its own meow() method.
💡 Inheritance should model a genuine "is-a" relationship — a Cat IS AN Animal.
Correct! The parentheses mean Dog inherits from, and automatically gains, everything Animal defines.
Not quite — class Dog(Animal): means Dog inherits from Animal.
2. Why can dog.speak() be called even though speak() is not defined inside Dog?
Correct! A subclass automatically inherits every method its parent class defines.
Not quite — Dog automatically inherits every method defined on Animal, its parent class.
3. Does Python support inheriting from more than one class at once?
Correct! Python fully supports multiple inheritance, listing several parent classes in the parentheses.
Not quite — Python does support multiple inheritance, e.g. class Dog(Animal, Pet):.
Quiz complete!
Chapter 5
30. Polymorphism
One interface, many implementations.
Code
python
1class Bird:23defsound(self):4print("Chirp")567class Cat:89defsound(self):10print("Meow")111213animals = [Bird(), Cat()]1415for animal in animals:16 animal.sound()
Explanation
Polymorphism means different objects can respond to the same method call in their own way — "one interface, many behaviors." Bird and Cat are completely unrelated classes, yet both define a .sound() method.
for animal in animals: animal.sound() calls .sound() on each object without knowing or caring which class it is — this works because of duck typing: if an object has the method you're calling, it works, regardless of class or ancestry.
Unlike inheritance (Chapter 29), polymorphism here doesn't require Bird and Cat to share any common parent class at all — Python checks for the method at runtime, not compile time.
How to Use & Where is Used
How to use: Give unrelated classes a method with the same name, then call that method on each object in a loop — Python doesn't care which class it belongs to, only that the method exists.
Where is used: Writing generic code that works with many different object types, and any collection of varied objects sharing a common method name.
Common in: A game engine calling .render() on a list of many different unrelated game object types, as long as each one happens to implement .render().
Example
Because Python checks method calls at runtime, a typo in a method name won't be caught until that exact line runs — thorough testing matters more than in statically-typed languages.
python — interactive shell
1>>>class Circle:2...defarea(self):3...return3.144...5>>>class Square:6...defarea(self):7...return48...9>>> shapes = [Circle(), Square()]10>>>for shape in shapes:11...print(shape.area())12...133.14144
Circle and Square share no common parent, yet the same loop calls .area() on both — each object responds in its own way to the identical method call.
Practice Exercises
Exercise 1 — Shared Method Name:
Define classes Car and Boat, each with a method travel() that prints a different message. Put one of each in a list and call .travel() on both without checking their types.
💡 Give both classes a method with the exact same name — Python doesn't need them to share a parent class.
python
1class Car:23deftravel(self):4print("Driving on the road")567class Boat:89deftravel(self):10print("Sailing on water")111213vehicles = [Car(), Boat()]1415for vehicle in vehicles:16 vehicle.travel()
Exercise 2 — Add a Third Class:
Add a class Fish (with a .sound() method that prints "Blub") to the animals list from the lesson, alongside Bird and Cat, and confirm the same loop still works for all three.
💡 As long as Fish also defines a .sound() method, it slots right into the same loop — no other changes needed.
python
1class Fish:23defsound(self):4print("Blub")567animals = [Bird(), Cat(), Fish()]89for animal in animals:10 animal.sound()
Exercise 3 — Shape Areas:
Define classes Rectangle (width, height set in __init__) and Circle (radius set in __init__), each with an area() method. Put one of each in a list and print each area in a loop.
💡 Rectangle area is width * height; circle area is 3.14 * radius * radius.
Encapsulation means bundling data with the methods that operate on it, and restricting direct access to that data from outside the class.
self.__balance uses a double leading underscore, which triggers Python's "name mangling" — internally renaming it to _BankAccount__balance, making it much harder to access accidentally from outside the class.
deposit() and get_balance() are the public gateway for outside code to change or read the balance — nothing outside the class can set __balance to an arbitrary value directly.
How to Use & Where is Used
How to use: Prefix an attribute with a double underscore (or a single one, by convention) to signal it's internal, then expose controlled public methods like deposit() to read or change it safely.
Where is used: Any class protecting its internal state from invalid changes — banking systems, game stats, and inventory counts.
Common in: A BankAccount class exposing deposit() and withdraw() methods instead of a public balance attribute, so external code can never set the balance to an arbitrary value directly.
Example
Python's privacy is convention-based rather than strictly enforced — a single leading underscore (_balance) signals "internal use only" and is actually more common in idiomatic Python than double underscores.
The double underscore isn't true enforced privacy — it's still reachable via its mangled name, _BankAccount__balance. It's a safety net that discourages accidental access, not a hard security boundary.
Practice Exercises
Exercise 1 — Basic Encapsulation:
Define a class Wallet with a private __cash attribute starting at 0, a method add_cash(amount) that increases it, and a method get_cash() that returns it. Create a wallet, add 200, and print the balance.
💡 Use self.__cash (double underscore) inside __init__, and only change it through add_cash().
Exercise 2 — Validated Withdrawal:
Add a withdraw(amount) method to BankAccount that only subtracts the amount if there's enough balance, otherwise prints "Insufficient funds". Test it by depositing 100 then trying to withdraw 200.
💡 Check amount > self.__balance inside withdraw() before subtracting anything.
Exercise 3 — Single Underscore Convention:
Define a class Employee with a single-underscore attribute _salary set in __init__, and a method get_salary() that returns it. Create an employee with salary 50000 and print it using the method.
💡 A single underscore (_salary) is the more common, idiomatic Python convention for "internal use only."
1. What does Python's "name mangling" do to an attribute like __balance?
Correct! Name mangling renames a double-underscore attribute to _ClassName__attribute internally, discouraging accidental access from outside.
Not quite — name mangling renames the attribute internally to _ClassName__balance; it doesn't delete or lock it.
2. Is a double-underscore attribute truly impossible to access from outside the class?
Correct! Name mangling is a safety net, not true enforced privacy — the mangled name is still reachable directly.
Not quite — a double-underscore attribute can still be accessed via its mangled name; it's a deterrent, not an absolute lock.
3. Why is get_balance() considered better practice than a public balance attribute?
Correct! A method gives you a controlled gateway — you can add validation, logging, or change the internal representation later without breaking outside code.
Not quite — the real benefit is control: a method lets you validate, log, or change internals later without breaking any code that calls it.
Quiz complete!
Chapter 6
32. File Handling
Reading from and writing to files.
Code
python
1withopen("notes.txt", "w") as file:2 file.write("Hello, Python!")34withopen("notes.txt", "r") as file:5 content = file.read()67print(content)
Explanation
open("notes.txt", "w") opens (creating it if needed) notes.txt in write mode — "w" replaces the file's entire contents.
The with statement is a context manager — it automatically closes the file afterward, even if an error happens partway through.
Opening the same file again in "r" (read) mode and calling .read() loads the whole file back as a single string.
How to Use & Where is Used
How to use:open(filename, mode) — common modes are "r" (read), "w" (write, overwrites), and "a" (append) — always wrapped in a with block so the file closes automatically.
Where is used: Saving user data between program runs, reading configuration, and processing logs or datasets stored on disk.
Common in: A program that reads a list of usernames from a .txt file, or appends a new line to a running log file every time it's called.
Example
"a" (append) mode adds to the end of the file instead of erasing what's already there.
python — interactive shell
1>>>withopen("log.txt", "a") as file:2... file.write("New entry\n")3...4>>>withopen("log.txt", "r") as file:5...print(file.read())6...7New entry
"a" mode adds to the end of the file instead of erasing what's already there — useful for logs that should keep growing over time.
Practice Exercises
Exercise 1 — Write to a File:
Open a file called "greeting.txt" in write mode and write the text "Hi there!" into it.
💡 open("greeting.txt", "w") as file: then file.write(...).
python
1withopen("greeting.txt", "w") as file:2 file.write("Hi there!")
Exercise 2 — Read a File:
Open "greeting.txt" in read mode, read its entire contents into a variable, and print that variable.
💡 open(..., "r") as file: then content = file.read().
python
1withopen("greeting.txt", "r") as file:2 content = file.read()34print(content)
Exercise 3 — Append to a File:
Open "greeting.txt" in append mode and add " See you soon!", without erasing what's already there.
💡 "a" mode adds to the end instead of overwriting.
python
1withopen("greeting.txt", "a") as file:2 file.write(" See you soon!")
Quick Quiz
3 questions · File Handling
1. What does the with statement do when working with files?
Correct! with guarantees the file gets closed, error or not.
Not quite — with automatically closes the file afterward, even if an error occurs.
2. What's the difference between "w" mode and "a" mode?
Correct! "w" overwrites; "a" appends without erasing.
Not quite — "w" overwrites the file, while "a" adds to the end.
3. What does file.read() return?
Correct! .read() loads the whole file back as one string.
Not quite — .read() returns the entire file's contents as a single string.
Quiz complete!
Chapter 6
33. Exceptions
Catching and handling runtime errors.
Code
python
1try:2 number = int("abc")3except ValueError:4print("That's not a valid number!")56print("Program continues...")
Explanation
Code inside try: runs normally until something goes wrong — int("abc") raises a ValueError because "abc" can't be converted to a number.
except ValueError: catches that specific error and runs its block instead of letting the program crash.
Because the exception was caught, "Program continues..." still runs — without try/except, the program would stop immediately at the error.
How to Use & Where is Used
How to use: Wrap risky code in try:, catch the specific error type you expect in except:, and optionally add finally: for code that always runs.
Where is used: Anywhere a runtime error is a realistic possibility — user input, file access, network requests, or external data that might be missing or malformed.
Common in: Validating user input in a loop, retrying a failed network request, or safely cleaning up a resource in a finally block no matter what happened.
Example
Catch specific exception types like ValueError or ZeroDivisionError rather than a bare except:, so unrelated bugs aren't silently hidden.
python — interactive shell
1>>>try:2... result = 10 / 03...except ZeroDivisionError:4...print("Can't divide by zero!")5...finally:6...print("Done trying.")7...8Can't divide by zero!9Done trying.
finally runs no matter what — whether the try block succeeded, failed, or was caught — making it the right place for cleanup code like closing a file or connection.
Practice Exercises
Exercise 1 — Catch a ValueError:
Write code that tries to convert the string "hello" to an integer inside a try block, and prints "Invalid number!" if it fails.
💡 except ValueError:
python
1try:2 number = int("hello")3except ValueError:4print("Invalid number!")
Exercise 2 — Catch a ZeroDivisionError:
Write code that tries to divide 10 by 0 inside a try block, and prints "Cannot divide by zero!" if it fails.
💡 except ZeroDivisionError:
python
1try:2 result = 10 / 03except ZeroDivisionError:4print("Cannot divide by zero!")
Exercise 3 — Use finally:
Write a try/except for dividing 10 by 0, and add a finally block that prints "Attempt finished" no matter what happens.
💡 finally: always runs, whether or not an exception occurred.
python
1try:2 result = 10 / 03except ZeroDivisionError:4print("Cannot divide by zero!")5finally:6print("Attempt finished")
Quick Quiz
3 questions · Exceptions
1. What happens to code inside a try block if no error occurs?
Correct! With no error, the try block just runs normally and every except is skipped.
Not quite — with no error, the try block runs normally and the except blocks are simply skipped.
2. What is the finally block used for?
Correct! finally always runs, error or not.
Not quite — finally always runs, whether or not an exception occurred.
3. Why is it better to catch a specific exception like ValueError rather than a bare except:?
Correct! A specific except type avoids silently swallowing unrelated bugs.
Not quite — a specific except only catches the error you expect, keeping other bugs visible.
Quiz complete!
Chapter 6
34. Regular Expressions
Pattern matching with the re module.
Code
python
1import re23text = "My phone number is 123-456-7890"4match = re.search(r"\d{3}-\d{3}-\d{4}", text)56print(match.group())
Explanation
import re loads Python's built-in regular expressions module, used for pattern matching inside text.
r"\d{3}-\d{3}-\d{4}" is a raw string pattern: \d means "any digit", and {3} means "exactly 3 of the previous thing" — so this pattern looks for three digits, a dash, three digits, a dash, four digits.
re.search() scans the text for the first place that pattern matches, and .group() returns the actual matched text.
How to Use & Where is Used
How to use: Write a pattern as a raw string r"...", then use re.search() for the first match, re.findall() for every match, or re.sub() to replace matches.
Where is used: Validating input formats (emails, phone numbers, postal codes), extracting specific pieces of text from a larger document, and flexible search-and-replace.
Common in: Checking whether a signup form's email field looks like a real email address before accepting it.
Example
Always check that a re.search() result isn't None before calling .group() on it — no match returns None.
python — interactive shell
1>>>import re2>>> text = "Call 111-222-3333 or 444-555-6666"3>>> re.findall(r"\d{3}-\d{3}-\d{4}", text)4['111-222-3333', '444-555-6666']
re.findall() returns every match in the text as a list, instead of stopping at the first one like re.search() does.
Practice Exercises
Exercise 1 — Find a Match:
Given text = "My email is jit@example.com", use re.search() with the pattern r"\w+@\w+\.\w+" to find the email address, then print match.group().
💡 re.search(pattern, text) returns a match object, or None if nothing matched.
python
1import re23text = "My email is jit@example.com"4match = re.search(r"\w+@\w+\.\w+", text)5print(match.group())
Exercise 2 — Find All Matches:
Given text = "Order 1, Order 2, Order 3", use re.findall() with the pattern r"\d+" to get every number as a list, then print it.
💡 \d+ means "one or more digits".
python
1import re23text = "Order 1, Order 2, Order 3"4numbers = re.findall(r"\d+", text)5print(numbers)
Exercise 3 — Replace a Match:
Given text = "I love cats", use re.sub() to replace "cats" with "dogs", and print the result.
💡 re.sub(pattern, replacement, text)
python
1import re23text = "I love cats"4result = re.sub(r"cats", "dogs", text)5print(result)
Quick Quiz
3 questions · Regular Expressions
1. What does \d{3} match in a regular expression?
Correct! \d means "any digit" and {3} means "exactly three of it".
Not quite — \d{3} matches exactly three digits.
2. What does re.findall() return?
Correct! re.findall() returns every match as a list.
Not quite — re.findall() returns a list of every match found, not just the first.
3. What should you check before calling .group() on the result of re.search()?
Correct! A failed re.search() returns None, and calling .group() on that crashes.
Not quite — always check the result isn't None before calling .group().