Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c54ba92
Update .gitignore to exclude Python and Java files
Iswanna Apr 11, 2026
1ae7aab
Add why_we_use_types.py exercise with bug identification and fix
Iswanna Apr 20, 2026
2fc8ae2
Rename why_we_use_types.py to why_we_use_types_exercise2.py
Iswanna Apr 20, 2026
1a7c06b
Add why_we_use_types_exercise1.py with type behavior demonstration
Iswanna Apr 20, 2026
1aca968
Fix type annotations and bugs in type_checking_with_mypy.py
Iswanna Apr 20, 2026
6f0982b
Add classes_and_objects_exercise1.py: demonstrate mypy type checking
Iswanna Apr 22, 2026
21fdd0a
Add classes_and_objects_exercise2.py: demonstrate mypy type checking
Iswanna Apr 22, 2026
01fb96a
Add methods-exercise1.md: advantages of using methods vs free functions
Iswanna Apr 22, 2026
4876f0d
Add methods-exercise2.py: refactor Person class to use date_of_birth
Iswanna Apr 22, 2026
ffc50f6
Add dataclasses-exercise.py: Person class using @dataclass decorator
Iswanna Apr 22, 2026
d1882e6
Add generics-exercise1.py: demonstrate generic types with Person class
Iswanna Apr 22, 2026
3c7f26a
Add type-guided-refactoring-exercise.py: refactor using generic List …
Iswanna Apr 22, 2026
535683f
Add enums-exercise.py: library laptop matching system with type-safe …
Iswanna Apr 22, 2026
67c5dff
Add interactive user input to enums-exercise.py
Iswanna Apr 22, 2026
0f884bd
Improve enums-exercise.py: enhance output messaging and formatting
Iswanna Apr 22, 2026
120cd4f
That's why those lines errored - you were trying to use Child-only me…
Iswanna Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules
.venv
*.class
17 changes: 17 additions & 0 deletions prep/classes_and_objects_exercise1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Person:
def __init__(self, name: str, age: int, preferred_operating_system: str):
self.name = name
self.age = age
self.preferred_operating_system = preferred_operating_system

imran = Person("Imran", 22, "Ubuntu")
print(imran.name)
print(imran.address)

eliza = Person("Eliza", 34, "Arch Linux")
print(eliza.name)
print(eliza.address)

# Answer
# The error returned after running mypy classes_and_objects_exercise1.py means that the Person class does not have an address attribute.
# An instance of Person was created with name, age and preferred_operating_system only.
22 changes: 22 additions & 0 deletions prep/classes_and_objects_exercise2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Person:
def __init__(self, name: str, age: int, preferred_operating_system: str):
self.name = name
self.age = age
self.preferred_operating_system = preferred_operating_system

imran = Person("Imran", 22, "Ubuntu")
print(imran.name)


eliza = Person("Eliza", 34, "Arch Linux")
print(eliza.name)

def is_adult(person: Person) -> bool:
return person.age >= 18

print(is_adult(imran))

def is_adult_wrong_attribute(person: Person) -> bool:
return person.address

print(is_adult_wrong_attribute(imran))
26 changes: 26 additions & 0 deletions prep/dataclasses-exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from datetime import date
from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
name: str
date_of_birth: date
preferred_operating_system: str

def is_adult(self) -> bool:
today = date.today()
age = today.year - self.date_of_birth.year

# Check if birthday has occurred this year
if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day):
age -= 1

return age >= 18

imran = Person("Imran", date(2002, 5, 15), "Ubuntu")
print(imran.name)
print(imran.is_adult())

eliza = Person("Eliza", date(1990, 3, 20), "Arch Linux")
print(eliza.name)
print(eliza.is_adult())
73 changes: 73 additions & 0 deletions prep/enums-exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from dataclasses import dataclass
from enum import Enum
from typing import List

class OperatingSystem(Enum):
MACOS = "macOS"
ARCH = "Arch Linux"
UBUNTU = "Ubuntu"

@dataclass(frozen=True)
class Person:
name: str
age: int
preferred_operating_system: OperatingSystem


@dataclass(frozen=True)
class Laptop:
id: int
manufacturer: str
model: str
screen_size_in_inches: float
operating_system: OperatingSystem


def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]:
possible_laptops = []
for laptop in laptops:
if laptop.operating_system == person.preferred_operating_system:
possible_laptops.append(laptop)
return possible_laptops


def create_person_from_input(laptops: List[Laptop]) -> None:
name = input("Enter person's name: ")
age = int(input("Enter person's age: "))

print("Available operating systems:")
for os in OperatingSystem:
print(f" {os.name}: {os.value}")

os_choice = input("Enter preferred operating system (MACOS/ARCH/UBUNTU): ").upper()

try:
preferred_os = OperatingSystem[os_choice]
except KeyError:
print("Invalid operating system choice!")
return

person = Person(name=name, age=age, preferred_operating_system=preferred_os)
possible_laptops = find_possible_laptops(laptops, person)

print(f"\nPossible laptops for {person.name}: {possible_laptops}")
print(f"The library has {len(possible_laptops)} laptop(s) with {preferred_os.value}")

laptops = [
Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system=OperatingSystem.ARCH),
Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
]

people = [
Person(name="Imran", age=22, preferred_operating_system=OperatingSystem.UBUNTU),
Person(name="Eliza", age=34, preferred_operating_system=OperatingSystem.ARCH),
]

for person in people:
possible_laptops = find_possible_laptops(laptops, person)
print(f"Possible laptops for {person.name}: {possible_laptops}")

print("\n--- Create a new person ---")
create_person_from_input(laptops)
30 changes: 30 additions & 0 deletions prep/generics-exercise1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from dataclasses import dataclass
from typing import List
from datetime import date

@dataclass(frozen=True)
class Person:
name: str
date_of_birth: date
children: List["Person"]

def age(self) -> int:
today = date.today()
age = today.year - self.date_of_birth.year
if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day):
age -= 1
return age

fatma = Person(name="Fatma", date_of_birth=date(2015, 7, 22), children=[])
aisha = Person(name="Aisha", date_of_birth=date(2018, 3, 10), children=[])

imran = Person(name="Imran", date_of_birth=date(1985, 5, 15), children=[fatma, aisha])

def print_family_tree(person: Person) -> None:
print(person.name)
for child in person.children:
print(f"- {child.name} ({child.age()})")

print_family_tree(imran)


37 changes: 37 additions & 0 deletions prep/inheritance-exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class Parent:
def __init__(self, first_name: str, last_name: str):
self.first_name = first_name
self.last_name = last_name

def get_name(self) -> str:
return f"{self.first_name} {self.last_name}"


class Child(Parent):
def __init__(self, first_name: str, last_name: str):
super().__init__(first_name, last_name)
self.previous_last_names = []

def change_last_name(self, last_name) -> None:
self.previous_last_names.append(self.last_name)
self.last_name = last_name

def get_full_name(self) -> str:
suffix = ""
if len(self.previous_last_names) > 0:
suffix = f" (née {self.previous_last_names[0]})"
return f"{self.first_name} {self.last_name}{suffix}"

person1 = Child("Elizaveta", "Alekseeva")
print(person1.get_name())
#print(person1.get_full_name())
#person1.change_last_name("Tyurina")
print(person1.get_name())
#print(person1.get_full_name())

person2 = Parent("Elizaveta", "Alekseeva")
print(person2.get_name())
#print(person2.get_full_name())
#person2.change_last_name("Tyurina")
print(person2.get_name())
#print(person2.get_full_name())
27 changes: 27 additions & 0 deletions prep/methods-exercise1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Here are the advantages of using methods instead of free functions:

1. **Organization** - Methods are part of a class, so related code is organized together in one place.

2. **Access to object data** - Methods can directly access the object's attributes (like `person.age`). Free functions need the object passed as a parameter.

3. **Clearer intent** - When you see `person.is_adult()`, it's clear that you're asking "is this person an adult?" instead of `is_adult(person)`.

4. **Less parameter passing** - Methods use `self` automatically, so you don't need to pass the object as a parameter every time.

5. **Better grouping** - All actions for a `Person` are grouped together inside the `Person` class, not scattered around your code.

6. **Easier to maintain** - If you need to change how `is_adult()` works, you only need to update the method in one place inside the class.

7. **More readable** - `imran.is_adult()` is easier to read and understand than `is_adult(imran)`.

**Example comparison:**

```python
# Free function (current approach)
print(is_adult(imran))

# Method (better approach)
print(imran.is_adult())
```

The method version is clearer and more organized!
25 changes: 25 additions & 0 deletions prep/methods-exercise2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from datetime import date

class Person:
def __init__(self, name: str, date_of_birth: date, preferred_operating_system: str):
self.name = name
self.date_of_birth = date_of_birth
self.preferred_operating_system = preferred_operating_system

def is_adult(self) -> bool:
today = date.today()
age = today.year - self.date_of_birth.year

# Check if birthday has occurred this year
if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day):
age -= 1

return age >= 18

imran = Person("Imran", date(2002, 5, 15), "Ubuntu")
print(imran.name)
print(imran.is_adult())

eliza = Person("Eliza", date(1990, 3, 20), "Arch Linux")
print(eliza.name)
print(eliza.is_adult())
42 changes: 42 additions & 0 deletions prep/type-guided-refactoring-exercise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from dataclasses import dataclass
from typing import List

@dataclass(frozen=True)
class Person:
name: str
age: int
preferred_operating_systems: List[str]


@dataclass(frozen=True)
class Laptop:
id: int
manufacturer: str
model: str
screen_size_in_inches: float
operating_system: str


def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]:
possible_laptops = []
for laptop in laptops:
if laptop.operating_system in person.preferred_operating_systems:
possible_laptops.append(laptop)
return possible_laptops


people = [
Person(name="Imran", age=22, preferred_operating_systems=["Ubuntu", "Arch Linux"]),
Person(name="Eliza", age=34, preferred_operating_systems=["Arch Linux"]),
]

laptops = [
Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system="Arch Linux"),
Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="Ubuntu"),
Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="ubuntu"),
Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system="macOS"),
]

for person in people:
possible_laptops = find_possible_laptops(laptops, person)
print(f"Possible laptops for {person.name}: {possible_laptops}")
30 changes: 30 additions & 0 deletions prep/type_checking_with_mypy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
def open_account(balances: dict[str, int], name: str, amount: int) -> None:
balances[name] = amount

def sum_balances(accounts: dict[str, int]) -> int:
total = 0
for name, pence in accounts.items():
print(f"{name} had balance {pence}")
total += pence
return total

def format_pence_as_string(total_pence: int) -> str:
if total_pence < 100:
return f"{total_pence}p"
pounds = int(total_pence / 100)
pence = total_pence % 100
return f"£{pounds}.{pence:02d}"

balances: dict[str, int] = {
"Sima": 700,
"Linn": 545,
"Georg": 831,
}

open_account(balances, "Tobi", 913)
open_account(balances, "Olya", 713)

total_pence: int = sum_balances(balances)
total_string: str = format_pence_as_string(total_pence)

print(f"The bank accounts total {total_string}")
20 changes: 20 additions & 0 deletions prep/why_we_use_types_exercise1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def half(value):
return value / 2

def double(value):
return value * 2

def second(value):
return value[1]


print(double("22"))

# Question
# Predict what double("22") will do. Then run the code and check. Did it do what you expected? Why did it return the value it did?

# Answer
# The function double("22") will return "2222".
# When I ran the program, it returned what I expected.
# The return value is "2222" because the argument is a string, so the * operator duplicates the string instead of
# multiplying a number. Python repeats the string twice: "22" + "22" = "2222".
14 changes: 14 additions & 0 deletions prep/why_we_use_types_exercise2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Question

def double(number):
return number * 3

print(double(10))

# Read the above code and write down what the bug is. How would you fix it?


# Answer
# The bug: The function multiplies the number by 3 instead of 2, which contradicts its name.

# Proposed fix: We can either change the function implementation so that it returns number * 2 or we change the function name to 'triple'
Loading