Difficulty: Beginner–Intermediate | Prerequisites: Basic Python syntax, variables, functions
These two challenges teach you to build Python classes that hold data and do something useful with it. Challenge 1 creates a Person class that stores a name, country and date of birth, then calculates the person's current age. Challenge 2 creates a Bank class that can open accounts, accept deposits, process withdrawals and report a balance. Both are exercises in defining __init__, writing instance methods and calling those methods on objects you create.
Class
A blueprint for creating objects. It bundles data (attributes) and behaviour (methods) into one reusable unit. In simple terms, a class is a template you write once and stamp out as many objects as you need.
Object (instance)
A concrete thing built from a class. When you write abdi = Person("Abdi Ali", "Somalia", 1977, 7, 7), abdi is an object (an instance of Person). Think of it as one filled-in copy of the template.
Attribute (instance variable)
A piece of data stored inside an object, set with self.name = name in __init__. Each object carries its own copy. In simple terms, attributes are the facts an object remembers about itself.
Method
A function defined inside a class. Its first parameter is always self, which gives it access to the object's attributes. Think of it as something the object knows how to do.
__init__ (constructor / initialiser)
The special method Python calls automatically when you create a new object. It sets up the object's starting attributes. In simple terms, it is the setup routine that runs the moment an object is born.
self
The reference every method uses to talk about the object it belongs to. It is always the first parameter of an instance method. Think of it as the object saying "me".
Default parameter
A value provided in the function signature (e.g. name = "?") so the caller can omit that argument. If nothing is passed, the default is used.
datetime.date / date.today()
Part of Python's built-in datetime module. date.today() returns today's date as a date object with .year, .month and .day attributes. Used in Challenge 1 to calculate age.
f-string (formatted string literal)
A string prefixed with f that lets you embed expressions inside curly braces: f"Balance: {amount}". The expression is evaluated at runtime and inserted into the string.
Create a Person class with attributes for name, country and date of birth, plus a calculate_age method that returns the person's current age as an integer.
Import date from the datetime module at the top of the file: from datetime import date
Define the class with class Person():
Write __init__ to accept name, country, year, month and day (the worksheet solution uses default values such as name = "?" and year = 0, but defaults are optional here)
Store every parameter on self: self.name = name, self.country = country, and so on
The key line in the worksheet's solution is:
age = today.year - birthDate.year - ((today.month, today.day) < (birthDate.month, birthDate.day))Breaking it down:
today.year - birthDate.year gives the rough age, assuming the birthday has already happened this year
(today.month, today.day) < (birthDate.month, birthDate.day) is a tuple comparison. Python compares tuples element by element, so this returns True (which Python treats as 1) if today's date has not yet reached the birthday month and day
Subtracting that boolean adjusts the age down by one if the birthday has not happened yet this year
This is a standard idiom for calculating age from a date of birth.
from datetime import date
class Person():
def __init__(self, name="?", country="?", year=0, month=0, day=0):
self.name = name
self.country = country
self.year = year
self.month = month
self.day = day
def calculate_age(self, birth_date):
today = date.today()
age = today.year - birth_date.year - (
(today.month, today.day) < (birth_date.month, birth_date.day)
)
return age
abdi = Person("Abdi Ali", "Somalia", 1977, 7, 7)
age = abdi.calculate_age(date(abdi.year, abdi.month, abdi.day))
print("Personal Info")
print(abdi.name)
print(abdi.country)
print(age)Personal Info
Abdi Ali
Somalia
49(The exact age depends on today's date.)
Age-from-DOB calculations appear everywhere: sign-up forms that enforce a minimum age, insurance premium calculators, healthcare systems that flag paediatric vs. adult patients. The tuple-comparison trick is the same logic those systems use.
Create a Bank class with four methods: create_account, make_deposit, make_withdrawal and check_balance. Then instantiate the class and call every method.
__init__ stores an account number (acc_num) and a starting deposit amount (deposit_amount)
The worksheet's solution uses input() inside methods so the user can type values at the console. This is fine for a classroom demo but would not appear in production code.
Each method prints a confirmation message using an f-string
create_account (called createAcc in the worksheet): prompts for a 7-digit PIN, stores it as the account number, and prints a confirmation with the deposit amount
make_deposit (called makeDeposit): prompts for an amount and an account number, then prints a deposit confirmation. A more complete version would also update self.deposit_amount.
make_withdrawal: subtracts from the balance after checking that funds are sufficient. The worksheet solution does not show this method, but the logic follows the same pattern: prompt, validate, update, print.
check_balance: prints the current account number and balance
class Bank:
def __init__(self, acc_num="", deposit_amount=1000):
self.acc_num = acc_num
self.deposit_amount = deposit_amount
def create_account(self):
num = input("Enter a 7-digit PIN to register your account: ")
self.acc_num = num
print(f"New A/C No: {num}")
print(f"Deposit Amount: £{self.deposit_amount}")
print("Account created successfully")
def make_deposit(self):
amount = int(input("Enter the amount you want to deposit: "))
self.deposit_amount += amount
print(f"Deposit: £{amount} to A/C No: {self.acc_num}")
print("Deposit successful.")
def make_withdrawal(self):
amount = int(input("Enter the amount you want to withdraw: "))
if amount > self.deposit_amount:
print("Insufficient funds.")
else:
self.deposit_amount -= amount
print(f"Withdrawal: £{amount} from A/C No: {self.acc_num}")
print("Withdrawal successful.")
def check_balance(self):
print(f"A/C No: {self.acc_num}")
print(f"Account balance: £{self.deposit_amount}")
bank = Bank("", 1000)
bank.create_account()
bank.make_deposit()
bank.check_balance()New A/C No: 5458845
Deposit Amount: £1000
Account created successfully
Deposit: £600 to A/C No: 5458845
Deposit successful.
A/C No: 5458845
Account balance: £1600This pattern, a class that wraps state (the balance) and exposes operations that change it (deposit, withdraw), is the foundation of any transactional system: banking APIs, e-commerce carts, inventory trackers. The principle is encapsulation: outsiders interact with the balance only through defined methods, never by editing the number directly.
Forgetting self as the first parameter. Every instance method needs self as its first argument. If you leave it out, Python will raise a TypeError about unexpected arguments when you call the method on an object.
Confusing the class with an object. Person is the blueprint; abdi is the object. You cannot call Person.calculate_age(...) without first creating an instance (unless you make it a class method or static method, which is a different topic).
Thinking __init__ is called manually. Students sometimes write abdi.__init__(...) after creating the object. Python calls __init__ automatically when you write Person(...). You almost never call it yourself.
Subtracting years without checking month and day. A naive today.year - birth_year is off by one for anyone whose birthday has not occurred yet this calendar year. The tuple comparison (today.month, today.day) < (birth.month, birth.day) handles this.
Mutating state without storing the result. In the Bank challenge, calling int(input(...)) and printing the amount is not the same as updating self.deposit_amount. If you forget the += or -=, the balance never changes.
⚠️ Classes and objects are foundational to every OOP question on the exam. Know how to define a class, write __init__, and create an instance.
⚠️ The self keyword appears in nearly every exam question about classes. Be ready to explain what it refers to and why it is required.
⚠️ The age-calculation pattern (year difference minus a boolean adjustment) is a commonly tested worked example. Understand the tuple comparison, not just the formula.
⚠️ Expect questions that ask you to trace through method calls: "What is the value of self.deposit_amount after these three operations?" Practice reading code line by line.
True or false: __init__ must be called explicitly after creating an object.
Fill in the blank: The first parameter of every instance method is ______.
True or false: (3, 15) < (3, 20) evaluates to True in Python.
If self.balance is 500 and you run self.balance -= 200, what is self.balance afterwards?
True or false: A class and an object are the same thing.
Answers: 1. False (Python calls it automatically). 2. self. 3. True (same first element, 15 < 20). 4. 300. 5. False (a class is a blueprint; an object is an instance of that blueprint).
Q: What does the expression (today.month, today.day) < (birth.month, birth.day) evaluate to, and why is it subtracted from the year difference?
A: It evaluates to True (i.e. 1) if today's month-day pair is earlier in the year than the birth month-day pair, meaning the person has not yet had their birthday this year. Subtracting 1 corrects the age down by one year.
Q: Write a Bank class method called check_balance that prints the account number and current balance.
A: def check_balance(self): print(f"A/C No: {self.acc_num}"); print(f"Balance: £{self.deposit_amount}")
Q: Why does every instance method take self as its first parameter?
A: self is how the method accesses the specific object it was called on. Without it, the method would have no way to read or modify that object's attributes.
Q: What happens if you call bank.make_withdrawal() and the withdrawal amount exceeds the balance?
A: A well-written method checks if amount > self.deposit_amount and prints an insufficient-funds message instead of allowing the balance to go negative.
Q: Explain why Person uses default parameter values like name = "?" in __init__.
A: Default values let you create a Person object without passing every argument. Any parameter you omit takes its default. This is useful when some attributes are optional or will be set later.
This material connects directly to inheritance and polymorphism (typically the next unit). Once you can define a class and create objects, the next step is making one class inherit from another, for example a Student class that extends Person with a student_id attribute. The encapsulation pattern in the Bank challenge also leads into data hiding and property decorators.
The datetime module reappears in file handling and data processing topics, so getting comfortable with date.today() and date arithmetic pays off beyond this worksheet.
Related Terms / Search Tags
Python class, OOP, object-oriented programming, __init__, constructor, self, instance, object, method, attribute, instance variable, datetime, date.today(), calculate age Python, Person class, Bank class, default parameters, encapsulation, PY101, Foundations of Computer Science