ap.Abhishek Patel

Search

Search notes, essays, projects, the library and the AI course. Use arrow keys to choose and Enter to open.

Lesson 1 of 10 30 min

Python basics: variables, collections, loops and functions

Write small Python programs with variables, the core data types, lists, tuples, sets and dictionaries, conditionals, loops, functions and imports, and explain why each fact should live in one place.

Words in this lessonVariable · Data type · List · Dictionary · Function · Scope · Module (Python)
Variable
A name you attach to a value so you can use it later. Like the price tag on a bakery tray: change the tag once and everyone who reads it sees the new price.
Data type
The kind of value something is, such as text, a whole number or true/false. Like measuring flour by weight and eggs by count: you cannot add a kilo to a dozen.
List
An ordered collection of values that you can add to, remove from and change. Like the ticket rail in a kitchen: orders in sequence, and you can add or pull tickets.
Dictionary
A collection that maps names (keys) to values, so you can look things up by name. Like the menu board: find the item name, read its price.
Function
A named, reusable block of code that takes inputs and gives back a result. Like a recipe card: written once, followed by anyone, with different ingredients each time.
Scope
The part of a program where a name can be seen and used. Like the pastry kitchen's bowls: they stay in the pastry kitchen, and only the finished cake comes out.
Module (Python)
A Python file whose code you can borrow and reuse in another file. Like a recipe binder on the shelf: you borrow the recipe instead of rewriting it.
On this page
  1. How to run the examples
  2. Arjun's first attempt: six copies of the same sum
  3. Name tags on trays: variables
  4. What kind of thing is it? Data types
  5. Strings: the raw material of AI apps
  6. Numbers, and why money is tricky
  7. Booleans: yes or no
  8. Converting between types
  9. Four ways to hold many things
  10. Lists: ordered and changeable
  11. Tuples: ordered and fixed
  12. Sets: unique values only
  13. Dictionaries: look things up by name
  14. Following Ananya's order through the program
  15. Deciding: if, elif, else
  16. Repeating: for and while
  17. Recipe cards: functions
  18. Parameters with defaults
  19. `return` is not `print`
  20. What stays in the kitchen: scope
  21. Borrowing recipe binders: imports and modules
  22. Where it breaks
  23. Arjun explains it to Meera
  24. Try it yourself
  25. Common mistakes
  26. Recap
  27. Go deeper

It’s four days before Diwali, and the FC Road store of Crumb & Co. is drowning in phone pre-orders. The store manager has written six of them on a notebook page and sent a photo to Meera, Crumb’s founder: who ordered what, and nothing else. Each customer needs a total by this evening, including the delivery fee. The rule: free delivery for orders of ₹1,000 or more, ₹30 for ₹500 or more, and ₹60 for anything smaller.

Meera forwards the photo to Arjun, the backend developer who joined Crumb’s new AI team last week. “You’re the programmer. Can Python do this before 5 p.m.?”

Arjun writes JavaScript all day. He did some Python in college, and he’s forgotten most of it. But how hard can six sums be?

Priya, the senior AI engineer who is mentoring him, watches from the next desk. She has a plan: let him try, then use whatever goes wrong to teach him the Python he’ll need for every AI tool in this course. The Claude and OpenAI libraries, Hugging Face, PyTorch and LangGraph all speak Python first.

Important

The idea in one sentence: Python programs are built from a handful of pieces: variables that put names on values, collections that hold many values, conditionals and loops that decide and repeat, and functions that package steps so you write them once and reuse them everywhere.

How to run the examples

Every example in this module is a complete program you can run. Save the code in a file, for example orders.py, then run it from your terminal (the text window where you type commands):

python3 orders.py

If you’ve already installed uv (a fast tool that manages Python projects; we set it up properly in the files and environments lesson), uv run orders.py works too. You can also type python3 on its own to open the REPL (Read, Evaluate, Print, Loop): an interactive prompt marked >>> where each line runs as soon as you press Enter. It’s perfect for quick experiments. Type exit() to leave.

Below each example you’ll see the exact output it prints, so you can check yours.

Arjun’s first attempt: six copies of the same sum

Arjun opens a file and types what feels natural: one line per order, with the prices typed straight in.

# Arjun's first attempt. Don't copy this one.
total1 = 2 * 80 + 1 * 450      # Ananya: 2 croissants, 1 plum cake
total2 = 3 * 120 + 4 * 80      # Rohan: 3 baguettes, 4 croissants
total3 = 1 * 450 + 6 * 90      # Kabir: 1 plum cake, 6 brownies
print("Ananya owes: " + total1)

Python refuses to run the last line:

TypeError: can only concatenate str (not "int") to str

He doesn’t know it yet, but two separate problems are hiding here. He fixes the first by guessing (str(total1)), finishes all six orders by hand, adds the delivery fees in his head, and sends the numbers to Meera at 3 p.m.

At 3:20 she replies: “Sorry, forgot to say: croissants went up to ₹85 this morning.” Arjun now has to hunt through his file for every 80 that means “croissant”. He changes four of them. He misses one. He also changes an 80 that wasn’t a croissant at all. Two customers get the wrong total.

Priya rolls her chair over. “The sums were never the hard part. The problem is that the price lives in nine places, and the rule for delivery lives in your head. Let’s put each fact in exactly one place.”

Name tags on trays: variables

“Look at the display counter,” Priya says. “Every tray has a little tag: ‘Croissant, ₹85’. When the price changes, what do they change, the croissants or the tag?”

“The tag.”

“Right. A variable is a name tag you stick on a value so you can refer to it later. Change the tag once, and everything that reads it sees the new value.”

croissant_price = 85
plum_cake_price = 450

ananya_total = 2 * croissant_price + 1 * plum_cake_price
print(ananya_total)

croissant_price = 90          # move the tag to a new value
print(croissant_price)
620
90

The = sign means “stick this name on this value”. It’s not “equals” in the maths sense, which is why price = price + 10 is perfectly sensible in Python: take the current value, add 10, and move the tag to the result.

Notice also that ananya_total stayed at 620 even after the croissant price changed. The calculation ran once, with the values at that moment. A variable doesn’t remember how it was calculated, only the value it ended up with.

Variable names use lowercase words joined by underscores (order_total, not OrderTotal). This style is called snake_case, and almost all Python code follows it.

What kind of thing is it? Data types

“Now, your TypeError,” Priya says. “Flour is measured by weight, eggs by count, and the ‘contains nuts’ label is yes or no. You can’t add a kilo of flour to ‘yes’. Python is the same.”

Every value has a data type: the kind of value it is, which decides what you can do with it. The ones you’ll use every day:

Type What it holds Example
str Text, called a string of characters "Eggless chocolate cake"
int Whole numbers 6
float Numbers with a decimal point 4.5
bool True or False True
NoneType Only the value None, meaning “nothing here” None
print(type("Plum cake"))
print(type(6))
print(type(4.5))
print(type(False))
print(type(None))
<class 'str'>
<class 'int'>
<class 'float'>
<class 'bool'>
<class 'NoneType'>

Arjun’s line "Ananya owes: " + total1 tried to join a str and an int. For text, + means “glue together”. For numbers, it means “add”. Python won’t guess which one you meant, so it stops. That’s a feature, not an annoyance: JavaScript would quietly glue them and carry on.

Strings: the raw material of AI apps

Every prompt you send to a large language model (an LLM, the kind of AI behind Claude and ChatGPT) is a string, and so is every answer it sends back. You’ll spend a lot of time building and cleaning text.

customer = "  Ananya  "
item = "Diwali hamper"
qty = 2

clean_name = customer.strip()           # remove spaces at both ends
print(clean_name.upper())
print(len(item))                        # number of characters
print("hamper" in item)                 # does the text contain "hamper"?
print(item.replace("Diwali", "Festive"))

message = f"Hi {clean_name}, your {qty} x {item} are on the way!"
print(message)
ANANYA
13
True
Festive hamper
Hi Ananya, your 2 x Diwali hamper are on the way!

The last one is an f-string (formatted string): put an f before the opening quote, and anything inside { } is replaced by its value, converted to text for you. This is the clean fix for Arjun’s error. It’s also exactly how most prompts are built in real AI apps: a template with values dropped in.

Numbers, and why money is tricky

total = 450
people = 4

print(total / people)    # true division: always gives a float
print(total // people)   # floor division: the whole part only
print(total % people)    # remainder, called "modulo"
print(round(112.456, 2))
print(0.1 + 0.2)
112.5
112
2
112.46
0.30000000000000004

That last line surprises everyone once. It isn’t a Python bug. Computers store decimals in binary, and some, like 0.1, can’t be stored exactly, the same way 1/3 can’t be written exactly in decimal (0.3333…). For money, store whole paise as int (₹80.50 becomes 8050), or use Python’s decimal module. Crumb’s prices are whole rupees, so int is fine here.

Booleans: yes or no

Comparisons produce booleans, and and, or and not combine them. This is how the delivery rule will be written.

subtotal = 610
is_member = False

print(subtotal >= 500)                 # greater than or equal
print(subtotal == 610)                 # == compares; = assigns
print(subtotal != 610)                 # != means "not equal"
print(subtotal >= 500 and is_member)   # both must be True
print(subtotal >= 500 or is_member)    # at least one must be True
print(not is_member)
True
True
False
False
True
True

Converting between types

Text from users, files and the web usually arrives as str, even when it looks like a number. A WhatsApp message that says “3” is the string "3", not the number 3. Convert before doing maths:

qty = int("3")
print(qty * 85)
print("Order #" + str(1042))
print(float("4.5"))
255
Order #1042
4.5

Four ways to hold many things

“Six orders means six of everything,” Priya says. “Six names, six carts. You don’t want total1 to total6. You want one name for the whole tray.” Python has four built-in collections, and each matches something on the bakery floor:

  • A list is the ticket rail in the kitchen: orders in sequence, and you can add, remove or reorder them.
  • A tuple is a printed receipt: an ordered record that never changes once printed.
  • A set is a guest list: each name counts once, however many times it’s written.
  • A dictionary is the menu board: look up a name, get its price.

Lists: ordered and changeable

rail = ["croissant", "plum cake", "baguette"]

print(rail[0])          # positions start at 0, not 1
print(rail[-1])         # negative positions count from the end
rail.append("brownie")  # add to the end
print(rail)
print(len(rail))
print(rail[1:3])        # a "slice": positions 1 and 2, stopping before 3
print("baguette" in rail)
croissant
baguette
['croissant', 'plum cake', 'baguette', 'brownie']
4
['plum cake', 'baguette']
True

Tuples: ordered and fixed

line = ("croissant", 2)          # one line of an order: (item, quantity)
item, qty = line                 # "unpacking" into two variables at once
print(item)
print(qty)
croissant
2

A cart is a list of these (item, qty) tuples. Tuples suit small records that shouldn’t change by accident, and functions use them to hand back two or three values at once.

Sets: unique values only

pickups = ["Baner", "FC Road", "Baner", "Kothrud", "FC Road"]
stores = set(pickups)
print(len(stores))
print("Kothrud" in stores)
3
True

A set drops duplicates and forgets order. Checking in on a set is very fast, even with millions of items, which is why sets are used to remember “IDs we’ve already processed”.

Dictionaries: look things up by name

This is the cure for Arjun’s nine copies of the croissant price.

menu = {
    "croissant": 85,
    "plum cake": 450,
    "baguette": 120,
    "brownie": 90,
}

print(menu["plum cake"])           # look up a value by its key
menu["kaju katli"] = 650           # add a new entry
menu["croissant"] = 80             # change an existing one
print(menu.get("donut"))           # .get gives None instead of crashing
print(menu.get("donut", 0))        # ...or a default you choose

for item, price in menu.items():   # loop over key-value pairs
    print(f"{item}: ₹{price}")
450
None
0
croissant: ₹80
plum cake: ₹450
baguette: ₹120
brownie: ₹90
kaju katli: ₹650

The name on the left of each pair is the key; what it points to is the value. Keys are unique, and dictionaries keep the order you added things.

Collection Looks like Ordered? Changeable? Duplicates? Typical use
list [1, 2] Yes Yes Yes Orders, chat messages, search results
tuple (1, 2) Yes No Yes Small fixed records, returning several values
set {1, 2} No Yes No Unique items, fast “have we seen this?” checks
dict {"a": 1} Yes Yes Keys: no Lookups by name, JSON-like records

You’ll meet the dictionary everywhere in AI work. A conversation with an LLM is sent as a list of dictionaries, like [{"role": "user", "content": "Do you have eggless cakes?"}]. You’ll write that shape hundreds of times in this course.

Note

Pause and think: Arjun stores a cart as cart = [("croissant", 2), ("plum cake", 1)]. What does cart[1][0] give you? And why is the menu a dictionary but the cart a list?

Show the answer

cart[1] is the second tuple, ("plum cake", 1), and position 0 of that is "plum cake". Read chains of brackets left to right.

The menu is a dictionary because you look prices up by name: “what does a croissant cost?” The cart is a list because order and repetition matter: a customer could write “croissant” on two separate lines, and a dictionary would only keep one of them.

Following Ananya’s order through the program

Now Priya and Arjun write the real thing together. Before looking at the code, follow one order through it by hand. Ananya wants 2 croissants and 1 plum cake. The menu says croissants are ₹85 and plum cake is ₹450.

Step What happens Values afterwards
1 Start with nothing subtotal = 0
2 First line of the cart: ("croissant", 2) item = "croissant", qty = 2
3 Look up the price in the menu menu["croissant"] is 85
4 Multiply and add 85 * 2 = 170, so subtotal = 170
5 Second line: ("plum cake", 1) item = "plum cake", qty = 1
6 Look up, multiply, add 450 * 1 = 450, so subtotal = 620
7 Delivery rule: is 620 ≥ 1000? No
8 Is 620 ≥ 500? Yes, so fee = 30
9 Final total 620 + 30 = 650
10 Build the message "Ananya: ₹650 (incl. ₹30 delivery)"

Steps 2 to 6 repeat the same thing for every line: that’s a loop. Steps 7 and 8 choose between options: that’s a conditional. And the whole thing will run six times, once per customer: that’s a function, called six times. Let’s build each piece.

Deciding: if, elif, else

A delivery rider decides at each junction: left or right? if, elif (short for “else if”) and else let your code decide.

subtotal = 620

if subtotal >= 1000:
    fee = 0
elif subtotal >= 500:
    fee = 30
else:
    fee = 60

print(f"Delivery fee: ₹{fee}")
Delivery fee: ₹30

Python checks the conditions top to bottom and runs the first block whose condition is True, then skips the rest. Order matters: if you checked >= 500 first, a ₹1,200 order would match it and wrongly pay ₹30.

Two details. Each condition ends with a colon :. And the lines that belong to it are indented by four spaces. In Python, indentation isn’t decoration. It’s how Python knows which lines belong to which block, the job { } braces do in JavaScript.

Repeating: for and while

for walks through a collection, one item at a time, running the indented block for each. Here’s steps 1 to 6 of the trace, as code:

menu = {"croissant": 85, "plum cake": 450, "baguette": 120, "brownie": 90}
cart = [("croissant", 2), ("plum cake", 1)]

subtotal = 0
for item, qty in cart:              # unpack each (item, qty) tuple
    line_total = menu[item] * qty
    print(f"{qty} x {item} = ₹{line_total}")
    subtotal = subtotal + line_total

print(f"Subtotal: ₹{subtotal}")
2 x croissant = ₹170
1 x plum cake = ₹450
Subtotal: ₹620

The same values as the hand trace, which is exactly the point of tracing by hand first. Three more loop tools you’ll see constantly:

for attempt in range(3):            # range(3) gives 0, 1, 2
    print("Attempt", attempt)

tickets = ["Late delivery", "Allergy question", "Refund"]
for number, ticket in enumerate(tickets, start=1):   # a counter and the value together
    print(number, ticket)

stock = 2
while stock > 0:                    # repeat as long as the condition is True
    stock = stock - 1
    print("Sold one, left:", stock)
Attempt 0
Attempt 1
Attempt 2
1 Late delivery
2 Allergy question
3 Refund
Sold one, left: 1
Sold one, left: 0

Inside any loop, break stops the loop early, and continue skips to the next item. Use while when you don’t know in advance how many times you’ll repeat (retrying until something succeeds, say), and for when you’re walking through a collection.

Recipe cards: functions

“Now,” Priya says, “you’re about to copy that loop six times. Don’t. What does the kitchen do with a recipe it makes every day?”

“Writes it on a card.”

“And then anyone can follow it with any ingredients. That’s a function: a named block of steps that takes inputs and hands back a result.”

def delivery_fee(subtotal):
    """Return the delivery fee for an order subtotal, in rupees."""
    if subtotal >= 1000:
        return 0
    if subtotal >= 500:
        return 30
    return 60


def order_total(cart, menu):
    """Return (subtotal, fee, total) for a cart of (item, qty) tuples."""
    subtotal = 0
    for item, qty in cart:
        subtotal = subtotal + menu[item] * qty
    fee = delivery_fee(subtotal)
    return subtotal, fee, subtotal + fee


menu = {"croissant": 85, "plum cake": 450, "baguette": 120, "brownie": 90}
print(order_total([("croissant", 2), ("plum cake", 1)], menu))
print(order_total([("baguette", 3), ("croissant", 4)], menu))
print(order_total([("plum cake", 1), ("brownie", 6)], menu))
(620, 30, 650)
(700, 30, 730)
(990, 30, 1020)

The new pieces:

  • def starts a function, followed by its name and its parameters (the inputs it expects) in brackets.
  • The text in triple quotes is a docstring: a description for humans. Later in the course, LLMs will read docstrings too, to decide which of your functions to call.
  • return ends the function and sends a value back to whoever called it. return subtotal, fee, subtotal + fee sends back a tuple of three values.
  • A function can call another function: order_total calls delivery_fee.

Look at Kabir’s order (the third line): ₹990, ten rupees short of free delivery. That’s the kind of fact Meera loves. (“Tell him one more brownie gets him free delivery!”)

Parameters with defaults

Give a parameter a default value and callers can leave it out:

def line_total(price, qty, discount=0.0):
    """Price for one line of an order after a discount (0.1 means 10% off)."""
    return round(price * qty * (1 - discount), 2)

print(line_total(85, 2))
print(line_total(450, 1, discount=0.1))
170.0
405.0

return is not print

This trips up almost every beginner, so it gets its own section.

def total_with_print(price, qty):
    print(price * qty)        # shows the value, hands back nothing

def total_with_return(price, qty):
    return price * qty        # hands the value back

a = total_with_print(85, 2)
b = total_with_return(85, 2)
print("a is", a)
print("b is", b)
170
a is None
b is 170

print shows a value on your screen and forgets it. return hands it back so the rest of the program can use it. A function with no return hands back None. If a value is mysteriously None when you expected a number, a missing return is the first thing to check.

Note

Pause and think: Arjun writes fee = delivery_fee(subtotal) but his delivery_fee function uses print(30) instead of return 30. The screen shows 30, which looks right. What goes wrong on the very next line, total = subtotal + fee?

Show the answer

fee is None, because the function printed 30 but returned nothing. Then subtotal + fee tries to add an int and None, and Python stops with TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'. The printed 30 was a red herring: it came from inside the function and never reached fee.

What stays in the kitchen: scope

Arjun tries to print subtotal after calling order_total, outside the function, and gets an error. Priya explains with the kitchen again. “The pastry kitchen has its own bowls. When the pastry chef finishes, those bowls don’t appear on the front counter. Only the finished cake comes out.”

Variables created inside a function are local: they exist only while that function runs. Which names are visible where is called scope.

tax_rate = 0.05                 # global: defined at the top level of the file

def price_with_tax(price):
    tax = price * tax_rate      # can READ the global
    return price + tax          # tax is local to this function

print(price_with_tax(100))

try:
    print(tax)                  # tax only existed inside the function
except NameError as error:
    print("Error:", error)
105.0
Error: name 'tax' is not defined

(The try and except let us show the error without the program stopping. You’ll learn them properly in the errors lesson.)

This is a feature. Because each function keeps its own bowls, order_total can use a variable called subtotal without worrying that some other function also has one. The rule of thumb: pass values in as parameters and send results out with return. A function that quietly changes global variables is hard to test, because its result depends on things you can’t see in its call.

Borrowing recipe binders: imports and modules

Crumb’s kitchen doesn’t invent every recipe. There’s a shelf of binders, and you borrow from them. In Python, a module is a file of code you can borrow from with import. Python comes with a large standard library of modules, and you can install thousands more.

import math
from datetime import date

print(math.ceil(7 / 3))                     # round UP: boxes for 7 cakes, 3 per box
print(date(2026, 11, 8).strftime("%A"))     # which weekday is Diwali 2026?
3
Sunday

import math brings in the whole module, and you use its contents as math.ceil. from datetime import date brings in one name directly.

Your own files are modules too. Arjun moves the menu and the two functions into crumb_utils.py:

# crumb_utils.py
MENU = {"croissant": 85, "plum cake": 450, "baguette": 120, "brownie": 90}


def delivery_fee(subtotal):
    if subtotal >= 1000:
        return 0
    if subtotal >= 500:
        return 30
    return 60


def order_total(cart, menu=MENU):
    subtotal = 0
    for item, qty in cart:
        subtotal = subtotal + menu[item] * qty
    fee = delivery_fee(subtotal)
    return subtotal, fee, subtotal + fee

Then a second file in the same folder uses it:

# preorders.py
from crumb_utils import order_total

preorders = [
    ("Ananya", [("croissant", 2), ("plum cake", 1)]),
    ("Rohan", [("baguette", 3), ("croissant", 4)]),
    ("Kabir", [("plum cake", 1), ("brownie", 6)]),
]

for name, cart in preorders:
    subtotal, fee, total = order_total(cart)
    print(f"{name}: ₹{total} (incl. ₹{fee} delivery)")
Ananya: ₹650 (incl. ₹30 delivery)
Rohan: ₹730 (incl. ₹30 delivery)
Kabir: ₹1020 (incl. ₹30 delivery)

MENU is in capitals as a signal to other programmers: “this is a setting; don’t change it while the program runs”. Packages you install, like anthropic or pandas, are imported in exactly the same way. Later in the course, you’ll organise AI apps as folders of modules: one for prompts, one for model calls, one for data.

Where it breaks

At 4:10 p.m., Meera sends one more message: “Also, a seventh order came in: Diya, 2 donuts and a plum cake.” Arjun adds it and runs the script. It stops with:

KeyError: 'donut'

Crumb doesn’t sell donuts; the store manager meant brownies. menu[item] crashes when the key doesn’t exist. That crash is actually useful: it’s better than silently charging ₹0 for donuts. But it stopped all seven orders, not only Diya’s. Two ways to handle it: check first with if item not in menu:, or use menu.get(item) and deal with the None. You’ll practise the first in the exercise below, and learn the full toolkit in the errors lesson.

Other things that break beginners’ programs, which experienced engineers watch for:

  • Money in floats. 0.1 + 0.2 isn’t 0.3. Totals that are off by a paisa after thousands of orders come from this. Use whole numbers of paise, or decimal.
  • Text that looks like a number. A quantity typed into WhatsApp is "2", not 2. "2" * 85 doesn’t fail; it gives you "2" repeated 85 times. Convert with int() at the point where data comes in.
  • Silent wrong answers from order. Putting elif subtotal >= 500 above if subtotal >= 1000 gives every big order a ₹30 fee, with no error at all. Bugs that don’t crash are the expensive ones; test with a value in each range.

Arjun explains it to Meera

At 4:45, the seven totals go out, all correct. Meera stops by. “So what changed between your first try and this one?”

“Before, the croissant price was typed in nine places, and the delivery rule was in my head,” Arjun says. “Now the menu is a dictionary: one place, item name to price. Each order is a list of lines. A loop walks the lines and adds them up. The delivery rule is an if statement, written once. And the whole recipe is a function I call once per customer.”

“And if croissants go up again?”

“I change one number. Every total updates.”

Meera nods. “That’s the first thing you’ve said that I’d pay for.”

Try it yourself

Write order_total(cart, menu, loyalty_member=False) that:

  1. Skips any item not on the menu, printing a warning (this fixes Diya’s donuts).
  2. Gives loyalty members 10% off the subtotal.
  3. Returns the subtotal after any discount (no delivery fee this time).

Test it with:

menu = {"croissant": 85, "plum cake": 450, "baguette": 120}
cart = [("croissant", 2), ("donut", 1), ("plum cake", 1)]

Expected: a warning about donut, then 558.0 for a member and 620 for a non-member.

Show the answer
def order_total(cart, menu, loyalty_member=False):
    """Total a cart. Members get 10% off. Unknown items are skipped."""
    subtotal = 0
    for item, qty in cart:
        if item not in menu:
            print(f"Warning: {item} is not on the menu, skipping")
            continue
        subtotal = subtotal + menu[item] * qty
    if loyalty_member:
        return round(subtotal * 0.9, 2)
    return subtotal


menu = {"croissant": 85, "plum cake": 450, "baguette": 120}
cart = [("croissant", 2), ("donut", 1), ("plum cake", 1)]

print(order_total(cart, menu, loyalty_member=True))
print(order_total(cart, menu))
Warning: donut is not on the menu, skipping
558.0
Warning: donut is not on the menu, skipping
620

Notice the member total is a float (because of * 0.9) and the other is an int. Small type details like this matter when you later save results to a database or send them to an API.

Common mistakes

  • Using = when you mean ==. = assigns, == compares. if price = 85: is a syntax error.
  • Mixed or missing indentation. Python decides what’s inside an if, loop or function by indentation alone. Use four spaces, and let your editor insert them.
  • Counting from 1. The first item of a list is rail[0], and on a 3-item list, rail[3] is an IndexError.
  • Forgetting the colon. if, elif, else, for, while and def lines all end with :.
  • Using dict[key] for keys that might be missing. It raises KeyError. Use .get(key, default) when a missing key is normal, and dict[key] when it would be a real bug you want to hear about.
  • Shadowing built-in names. Calling a variable list, dict, sum or type hides Python’s own version, and code later in the file breaks in confusing ways.

Recap

  • A variable is a name tag on a value; change the value in one place and everything that reads the name sees it.
  • The everyday types are str, int, float, bool and None; Python won’t mix them silently, and f-strings are how you build text (and prompts) from values.
  • Lists are ordered and changeable, tuples are fixed records, sets keep unique items, and dictionaries look values up by key. AI chats are lists of dictionaries.
  • if/elif/else decide, for and while repeat, and indentation shows what belongs to what.
  • Functions take parameters and return a result; variables inside them are local; print is not return.
  • import borrows code from modules: Python’s standard library, installed packages, or your own files.

That evening, Priya pulls up a real order from Crumb’s online shop to show Arjun what’s coming. It isn’t a tidy tuple. It’s a dictionary with a customer inside it, a list of items inside that, and a delivery slot that says null. “The phone orders were the easy version,” she says. “Next, let’s handle real data the Pythonic way.”

Go deeper

Check your understanding

3 quick questions
1. Crumb's croissant price appears in nine places in Arjun's script. What is the best fix?
2. A function prints the correct total on screen, but the variable that receives its result is None. What is the most likely cause?
3. The delivery rule is checked in this order: if subtotal >= 500, fee is 30; elif subtotal >= 1000, fee is 0. What happens to a ₹1,200 order?