Lesson 2 · Python Beginner Course
Python Essentials: Think Like a Programmer (Beginner Python Course, Part 2)
In this lesson you'll be able to: Slice, search and reshape text with string methods Do maths without surprises, and convert between types safely Write…
- CoursePython Beginner Course
- Lesson2 of 10
- Video1 h 0 min
- FormatJupyter notebook · 84 code cells
What you'll learn
- Strings: Python's Text Superpowers
- String Methods: Clean, Search and Reshape Text
- Mini-Project: The Conference Name Badge
- Numbers Without Surprises
- Type Conversion: Crossing the Text and Number Border
- Mini-Project: The Road-Trip Fuel Splitter
- Smarter Decisions: Conditionals in Depth
- Loops in Depth: break, continue and for-else
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPython Essentials: Think Like a Programmer#
In this lesson you'll be able to:
- Slice, search and reshape text with string methods
- Do maths without surprises, and convert between types safely
- Write sharper decisions and loops with break, continue, enumerate and zip
- Package your logic into reusable functions with defaults and return values
- Borrow ready-made tools from the math and random modules
- Crack problems with a four-step method: understand, plan, code, test Projects we'll build: a name badge maker, a road-trip fuel splitter, a step-count report, and a Password Strength Coach
Warm-Up: A Two-Minute Refresher#
Print, variables, if and loops: the four tools you already own.
city = "Lisbon"
temperature = 24
print(city, "is", temperature, "degrees today")
if temperature > 20:
print("T-shirt weather")
else:
print("Bring a jacket")
for day in range(1, 4):
print(f"Day {day}: water the plants")
battery = 30
while battery > 0:
print(f"Battery at {battery}%")
battery -= 10
Strings: Python's Text Superpowers#
A string is a row of numbered lockers: one character per locker, and the numbering starts at zero.
print("She said \"hi\" and left.")
print('It\'s raining')
print("Line one\nLine two")
print("Name:\tPriya")
print("Saved in C:\\photos")
poem = """Roses need water,
code needs a test,
run it and read it,
then fix all the rest."""
print(poem)
first = "snow"
print(first + "ball")
print("ha" * 3)
print("=" * 20)
word = "PYTHON"
print(word[0])
print(word[2])
print(word[-1])
print(word[-2])
print(len(word))
Common mistake: asking for a locker that doesn't exist.
print(word[6])
print(word[len(word) - 1])
print(word[-1])
ticket = "SKY-2041-BLUE"
print(ticket[0:3])
print(ticket[4:8])
print(ticket[9:])
print(ticket[:3])
print(ticket[::-1])
print(ticket[4:100])
print(ticket[50:60] == "")
String Methods: Clean, Search and Reshape Text#
A method is a function that belongs to a value: value.method()
messy = " hello WORLD "
print(f"[{messy.upper()}]")
print(f"[{messy.lower()}]")
print(f"[{messy.strip()}]")
print(f"[{messy.strip().title()}]")
note = "I love tea. Tea is life. More tea please."
print(note.replace("tea", "coffee"))
print(note.lower().replace("tea", "coffee"))
print(note.lower().count("tea"))
groceries = "eggs,milk,bread,apples"
items = groceries.split(",")
print(items)
print(len(items), "items")
print(" | ".join(items))
print("Learn to code today".split())
filename = "holiday_photo_2024.jpg"
print(filename.find("photo"))
print(filename.find("video"))
print(filename.startswith("holiday"))
print(filename.endswith(".jpg"))
print("2024" in filename)
phrase = "Programming is a superpower"
vowels = 0
for char in phrase.lower():
if char in "aeiou":
vowels += 1
print(f"{vowels} vowels in {len(phrase)} characters")
Common mistake: trying to change a string in place. Strings are immutable: they never change.
name = "kate"
name[0] = "K"
name = "K" + name[1:]
print(name)
shout = name.upper()
print(name, shout)
Pro tip: a method that seems to do nothing usually worked fine. You forgot to save its result: text = text.strip()
Your turn#
- Take " ADA lovelace " and print it as "Ada Lovelace" in one line of chained methods.
- Print whether the email "sam@example.org" ends with ".org".
Mini-Project: The Conference Name Badge#
Messy input in, tidy printed badge out: strip, title, split, slicing and center.
raw_name = input("Your full name: ")
raw_role = input("Your role: ")
clean_name = " ".join(raw_name.split()).title()
parts = clean_name.split()
initials = parts[0][0] + parts[-1][0]
print(clean_name, parts, initials)
width = 30
border = "+" + "-" * width + "+"
print(border)
print("|" + clean_name.center(width) + "|")
print("|" + raw_role.strip().upper().center(width) + "|")
print("|" + f"({initials})".center(width) + "|")
print(border)
Numbers Without Surprises#
int = whole numbers, float = numbers with a decimal point
print(7 / 2)
print(8 / 2)
print(7 // 2)
print(7 % 2)
print(-7 // 2)
print(2 ** 10)
minutes = 135
hours = minutes // 60
leftover = minutes % 60
print(f"{minutes} minutes is {hours}h {leftover}m")
print("Is 14 even?", 14 % 2 == 0)
print(round(3.14159, 2))
print(round(7.6))
print(round(2.5), round(3.5))
print(abs(-12))
print(abs(4 - 9))
import math
print(math.sqrt(81))
print(math.pi)
print(math.ceil(4.1), math.floor(4.9))
boxes = math.ceil(53 / 12)
print(f"53 mugs need {boxes} boxes of 12")
Watch out: computers store decimals in binary, and some can't be stored exactly.
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
total = 0.1 + 0.2
print(round(total, 2) == 0.3)
print(math.isclose(total, 0.3))
print(f"Total: {total:.2f}")
population = 1234567.891
print(f"{population:,.2f}")
print(f"{0.256:.1%}")
print(f"Order #{42:05}")
print(f"[{'left':<8}] [{'right':>8}] [{'mid':^8}]")
Pro tip: for money, count in pennies (whole numbers) and only divide by 100 when you print.
Type Conversion: Crossing the Text and Number Border#
int() and float() turn text into numbers; str() turns anything into text.
age_text = "25"
print(type(age_text))
print(age_text * 2)
print(int(age_text) * 2)
Common mistake: gluing text and numbers together with +
age = 25
print("Age: " + age)
print("Age: " + str(age))
print(f"Age: {age}")
print("Age:", age)
print(int("ten"))
print(float("3.5"))
print(int(float("3.5")))
print(int(3.99), round(3.99))
for text in ["42", "4.2", "forty", " 7"]:
print(f"'{text}' all digits? {text.isdigit()}")
print(bool("False"))
print(bool(""))
tickets = input("How many tickets? ")
print(type(tickets))
print(f"Total: £{int(tickets) * 12.5:.2f}")
Mini-Project: The Road-Trip Fuel Splitter#
Ask for distance, fuel use and price, then split the cost fairly between friends.
distance_km = float(input("Trip distance in km: "))
litres_per_100km = float(input("Litres used per 100 km: "))
price_per_litre = float(input("Fuel price per litre: "))
people = int(input("People sharing the cost: "))
litres_needed = distance_km * litres_per_100km / 100
total_cost = litres_needed * price_per_litre
each_pays = total_cost / people
tanks = math.ceil(litres_needed / 50)
print(f"Fuel needed: {litres_needed:.1f} litres")
print(f"Total cost: £{total_cost:.2f}")
print(f"Each pays: £{each_pays:.2f}")
print(f"Fill-ups of a 50 L tank: {tanks}")
Quick check#
What does 9 // 4 give? And what happens with int("9.0")?
Smarter Decisions: Conditionals in Depth#
Chained comparisons, truthy and falsy values, and the one-line if.
temperature = 18
if temperature >= 15 and temperature <= 25:
print("Long way: perfect for a walk")
if 15 <= temperature <= 25:
print("Chained: perfect for a walk")
uv_index = 7
if uv_index < 3:
advice = "No protection needed"
elif uv_index < 6:
advice = "Wear sunglasses"
elif uv_index < 8:
advice = "Sunscreen and a hat"
else:
advice = "Stay in the shade at midday"
print(f"UV {uv_index}: {advice}")
for value in [0, 7, "", "hi", [], [1, 2], None, 0.0]:
print(f"{repr(value):>6} is {bool(value)}")
nickname = ""
if nickname:
print(f"Hi {nickname}!")
else:
print("Hi there, stranger!")
answer = " YES "
if answer.strip().lower() in ["y", "yes", "yep", "sure"]:
print("Great, booking it now")
else:
print("No problem, maybe next time")
stock = 0
status = "In stock" if stock > 0 else "Sold out"
print(status)
count = 1
print(f"You have {count} {'item' if count == 1 else 'items'} in your basket")
has_ticket = True
age = 15
with_adult = True
if has_ticket and (age >= 16 or with_adult):
print("Enjoy the film!")
if not has_ticket:
print("Please visit the box office")
Pro tip: when you mix and with or, add brackets. They cost nothing and make your intent obvious to the next reader.
Loops in Depth: break, continue and for-else#
break = leave the loop now. continue = skip to the next round.
print(list(range(5)))
print(list(range(2, 11, 2)))
for count in range(3, 0, -1):
print(count, end="... ")
print("Lift off!")
readings = [21, 22, 23, 98, 24, 22]
for reading in readings:
if reading > 50:
print(f"Sensor fault: {reading}! Stopping.")
break
print("Reading OK:", reading)
emails = ["ana@mail.com", "", "ben@mail.com", "not-an-email", "cy@mail.com"]
for email in emails:
if "@" not in email:
continue
print("Sending invite to", email)
shelf = ["Dune", "Emma", "Ulysses"]
wanted = "Hamlet"
for title in shelf:
if title == wanted:
print("Found it!")
break
else:
print(f"Sorry, {wanted} is not on the shelf")
correct_pin = "4071"
attempts = 0
while attempts < 3:
guess = input("Enter PIN: ")
attempts += 1
if guess == correct_pin:
print("Unlocked!")
break
print(f"Wrong PIN. {3 - attempts} tries left.")
else:
print("Card locked.")
Common mistake: forgetting to indent the loop body.
for lap in range(3):
print("Lap", lap)
for lap in range(3):
print("Lap", lap + 1)
Loop Power Tools: Nested Loops, enumerate and zip#
booked = ["A2", "B3", "B4"]
for row in "ABC":
line = ""
for seat in range(1, 5):
seat_code = f"{row}{seat}"
line += " XX " if seat_code in booked else f" {seat_code} "
print(line)
for row in range(1, 6):
for col in range(1, 6):
print(f"{row * col:4}", end="")
print()
podium = ["Mia", "Theo", "Sana"]
for place, runner in enumerate(podium, start=1):
print(f"{place}. {runner}")
products = ["Notebook", "Pen", "Backpack"]
prices = [3.5, 1.2, 24.0]
for product, price in zip(products, prices):
print(f"{product:<10} £{price:>6.2f}")
days = ["Mon", "Tue", "Wed"]
temps = [14, 17]
print(list(zip(days, temps)))
Mini-Project: The Weekly Step-Count Report#
zip + conditionals + string repetition = a text bar chart with a goal tracker.
week = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
steps = [6200, 11050, 8400, 3900, 10230, 14800, 7600]
goal = 8000
for day, count in zip(week, steps):
bar = "#" * (count // 1000)
flag = "goal!" if count >= goal else ""
print(f"{day} {count:>6,} {bar:<15} {flag}")
days_hit = 0
for count in steps:
if count >= goal:
days_hit += 1
total = sum(steps)
best = max(steps)
best_day = week[steps.index(best)]
print(f"Total: {total:,} steps, about {total / len(steps):,.0f} a day")
print(f"Goal reached on {days_hit} of {len(week)} days")
print(f"Best day: {best_day} with {best:,} steps")
Functions: Write It Once, Use It Everywhere#
A function is a vending machine: put something in, press the button, get something out. You don't need to see the gears.
def print_banner(title):
"""Print a title between two lines of equals signs."""
print("=" * 24)
print(title.center(24))
print("=" * 24)
print_banner("Monday Plan")
print_banner("Shopping")
Parameter = the placeholder name in the definition (title). Argument = the actual value you pass in when you call it ("Monday Plan").
def celsius_to_fahrenheit(celsius):
"""Convert a Celsius temperature to Fahrenheit."""
return celsius * 9 / 5 + 32
summer = celsius_to_fahrenheit(30)
print(summer)
print(celsius_to_fahrenheit(-40))
def describe_temperature(celsius):
fahrenheit = celsius_to_fahrenheit(celsius)
feel = "warm" if celsius >= 20 else "cool"
return f"{celsius} C is {fahrenheit:.0f} F, which feels {feel}"
for reading in [8, 21, 35]:
print(describe_temperature(reading))
def add_vat_printing(price):
print(price * 1.2)
result = add_vat_printing(10)
print("Stored result:", result)
Common mistake: expecting a variable made inside a function to exist outside it.
def basket_total():
basket_sum = 4.99 + 2.50 + 1.25
basket_total()
print(basket_sum)
def basket_total():
return 4.99 + 2.50 + 1.25
basket_sum = basket_total()
print(f"Basket: £{basket_sum:.2f}")
help(celsius_to_fahrenheit)
Common mistake: calling a function without the arguments it needs.
celsius_to_fahrenheit()
Why functions matter:
- Name an idea once, then reuse it anywhere
- Fix a bug in one place, not ten
- Test small pieces on their own
- Read code like a story: check_rules(), score_password(), report()
Function Superpowers: Defaults, Keywords and Multiple Returns#
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Amara"))
print(greet("Amara", "Good morning"))
def order_coffee(size, milk="none", sugars=0):
return f"{size} coffee | milk: {milk} | sugars: {sugars}"
print(order_coffee("Large"))
print(order_coffee("Large", sugars=2))
print(order_coffee(milk="oat", size="Small"))
def summarise(numbers):
"""Return the lowest, highest and average of a list."""
return min(numbers), max(numbers), sum(numbers) / len(numbers)
low, high, average = summarise([12, 7, 19, 4])
print(f"Low {low}, high {high}, average {average}")
print(summarise([1, 2, 3]))
def safe_divide(a, b):
if b == 0:
return None
return a / b
print(safe_divide(10, 4))
print(safe_divide(5, 0))
Pro tip: a good function does one job, and its name is a verb that says what: convert_price(), count_vowels(), send_reminder().
Your turn#
Write a function minutes_to_text(minutes) that returns a string like "2h 15m".
A First Taste of *args#
A star before a parameter collects any number of arguments into a tuple.
def total_bill(*prices):
print("Received:", prices)
return sum(prices)
print(total_bill(4.5, 2.25, 3))
print(total_bill(10))
print(total_bill())
def cheer(punctuation, *words):
for word in words:
print(word.upper() + punctuation)
cheer("!", "ready", "steady", "go")
print("a", "b", "c", sep=" / ")
Borrowing Code: Importing math and random#
A module is a toolbox someone else has already built. import opens the lid.
import math
ladder = math.hypot(3, 4)
print(f"A ladder from 3 m out to 4 m up must be {ladder} m long")
from math import sqrt, pi
print(f"Circle area: {pi * 3 ** 2:.2f}")
print(sqrt(2))
import random
random.seed(42)
print(random.randint(1, 6))
print(random.choice(["pizza", "curry", "tacos", "ramen"]))
print(round(random.random(), 3))
players = ["Ava", "Ben", "Caz", "Dev", "Eli", "Fay"]
random.shuffle(players)
print("Red team: ", players[:3])
print("Blue team:", players[3:])
print("Captains:", random.sample(players, 2))
Pro tip: never name your own file random.py or math.py. Python will import YOUR file instead of the real module, and nothing will work.
The Problem-Solving Method: Understand, Plan, Code, Test#
- Understand: what goes in, what comes out? Write an example by hand.
- Plan: list the steps in plain English.
- Code: turn each step into a line or a small function.
- Test: try normal, tricky and empty inputs.
# Problem: find the longest word in a sentence
# Understand: "Coffee fuels late night coding" -> "Coffee"
plan = [
"Split the sentence into words",
"Remember the longest word so far (start with nothing)",
"For each word, if it's longer, remember it instead",
"Hand back the longest word",
]
for number, step in enumerate(plan, start=1):
print(f"Step {number}: {step}")
def longest_word(sentence):
"""Return the longest word; the first one wins a tie."""
longest = ""
for word in sentence.split():
word = word.strip(".,!?")
if len(word) > len(longest):
longest = word
return longest
print(longest_word("Coffee fuels late night coding"))
tests = [
("Coffee fuels late night coding", "Coffee"),
("Wow!", "Wow"),
("a bb ccc bb", "ccc"),
("", ""),
]
for sentence, expected in tests:
got = longest_word(sentence)
verdict = "PASS" if got == expected else "FAIL"
print(f"{verdict}: {sentence!r} -> {got!r}")
assert longest_word("Python is fun") == "Python"
assert longest_word("Hi, everyone!") == "everyone"
print("All checks passed")
Pro tip: stuck? Shrink the problem. Solve it for one word, then two, then a whole sentence.
Quick check#
In the problem-solving method, why do we plan in plain English before writing code?
Capstone: The Password Strength Coach#
Built from small functions, each doing one job:
- contains_any() -> check_rules() -> looks_common() -> score_password()
- strength_label() -> report() -> report_all(*passwords)
- make_passphrase() suggests strong passwords with random
import string
def contains_any(text, characters):
"""Return True if text contains at least one of the characters."""
for char in text:
if char in characters:
return True
return False
print(string.digits, string.ascii_uppercase)
print(contains_any("hello", string.digits), contains_any("hell0", string.digits))
def check_rules(password):
"""Return a list of (passed, tip) pairs, one per rule."""
return [
(len(password) >= 12, "Use at least 12 characters"),
(contains_any(password, string.ascii_lowercase), "Add a lowercase letter"),
(contains_any(password, string.ascii_uppercase), "Add a capital letter"),
(contains_any(password, string.digits), "Add a digit"),
(contains_any(password, string.punctuation), "Add a symbol like ! or ?"),
]
for rule in check_rules("sunshine"):
print(rule)
COMMON_WORDS = ["password", "qwerty", "123456", "letmein", "admin"]
def looks_common(password):
"""Return True if the password contains a well-known weak word."""
lowered = password.lower()
for word in COMMON_WORDS:
if word in lowered:
return True
return False
print(looks_common("MyPassword99"), looks_common("maple-Comet-71"))
def score_password(password):
"""Score 0-5: one point per rule passed, 0 if it uses a common word."""
if looks_common(password):
return 0
score = 0
for passed, tip in check_rules(password):
if passed:
score += 1
return score
def strength_label(score):
labels = ["Terrible", "Very weak", "Weak", "Fair", "Good", "Strong"]
return labels[score]
print(score_password("sunshine"), strength_label(score_password("sunshine")))
def report(password):
"""Print a strength meter and tips for one password."""
score = score_password(password)
meter = "#" * score + "-" * (5 - score)
print(f"{password:<32} [{meter}] {score}/5 {strength_label(score)}")
if looks_common(password):
print(" - Avoid well-known words like 'password'")
for passed, tip in check_rules(password):
if not passed:
print(" -", tip)
def report_all(*passwords):
for password in passwords:
report(password)
report_all("sunshine", "Password123!", "Tr0ub4dor&3", "blue-Kettle-7-Orbit")
WORDS = ["maple", "rocket", "lantern", "pepper", "harbor",
"violet", "comet", "biscuit", "glacier", "tundra"]
def make_passphrase(word_count=3, separator="-"):
"""Build a random passphrase like Maple-Comet-Tundra-42!"""
words = []
for word in random.sample(WORDS, word_count):
words.append(word.capitalize())
number = random.randint(10, 99)
return separator.join(words) + separator + str(number) + "!"
random.seed(7)
report_all(make_passphrase(), make_passphrase(4, separator="."))
while True:
attempt = input("Try a password (blank to stop): ")
if not attempt:
break
report(attempt)
if score_password(attempt) == 5:
print("Excellent, that one's a keeper!")
break
Recap#
- Strings: escapes, triple quotes, index from 0 (or -1 from the end), slice [start:stop:step]
- String methods return NEW strings: strip, lower, replace, split, join, find, startswith, in
- Numbers: / gives a float, // floors, % is the remainder; floats are approximate
- Convert with int(), float(), str(); input() always returns text
- Chained comparisons, truthy/falsy values, and x if condition else y
- break, continue, for-else, nested loops, enumerate and zip
- Functions: parameters, defaults, keywords, return (even several values), docstrings, *args
- import math and random; solve problems with understand, plan, code, test
Try it yourself#
- Coach upgrade: add a rule that fails if the password contains the same character 3 times in a row.
- Receipt printer: write line_item(name, price, qty=1) and receipt(*items) that print a neat, totalled receipt.
- Study planner: write plan_sessions(total_minutes, session=25) that returns how many sessions and the minutes left over.
What's next#
Lesson 3: Learn Python by Building Projects. You've got the essentials. Next, we put them to work on bigger, real-world programs.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



