Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
eb5b9e2
Writing bugs that I saw and the types as comments according to me.
SaraTahir28 Dec 2, 2025
d937285
first step of adding types. and running mypy on terminal
SaraTahir28 Dec 2, 2025
7dbbd28
Fixed all bug, also ran mypy with --strict flag to pick every issue. …
SaraTahir28 Dec 2, 2025
882b78f
read and understood the error
SaraTahir28 Dec 2, 2025
6771d14
No error mesages. mypy infers that person has a property of age
SaraTahir28 Dec 2, 2025
4e2e07e
It does report an error because cant access an attribute that is not …
SaraTahir28 Dec 2, 2025
77ca2f6
Refactor Person class: replace age field with date of birth parameter
SaraTahir28 Dec 2, 2025
6f00188
incorporating dataclasses in the code
SaraTahir28 Dec 2, 2025
2dc539b
testing for value based equality with dataclass.
SaraTahir28 Dec 2, 2025
2eb49e9
adding generic type to children:list and adding age for the children.
SaraTahir28 Dec 2, 2025
a5faf8b
Refactor Person to use preferred_operating_systems as List[str]
SaraTahir28 Dec 3, 2025
cfa95d6
Add laptop library with enum validation and OS availability check
SaraTahir28 Dec 4, 2025
7f31e92
Prediction is correct -> childclass methods cannot be inherited by su…
SaraTahir28 Dec 4, 2025
842c004
Removed the error causing lines
SaraTahir28 Dec 4, 2025
dfc29a8
Adding a print statement to tell about greater chnaces of getting a l…
SaraTahir28 Dec 4, 2025
af120e7
Solving 1- double exercises
SaraTahir28 Dec 5, 2025
b5b4a6f
making necessary changes based on code review
SaraTahir28 Jan 19, 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
20 changes: 20 additions & 0 deletions Sprint5/classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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)

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

print(is_adult(imran))

def works_at(person: Person) -> str:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what works_at is doing here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted the function. if I remember correctly I wanted to add a Jobcompany attribute but later decided against it and forgot to delete the function!

return person.jobcompany
25 changes: 25 additions & 0 deletions Sprint5/data-class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from datetime import date
from dataclasses import dataclass

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

def age(self)-> int: #returns age in years
today = date.today()
years = today.year - self.birth_day.year
if(today.month, today.day) < (self.birth_day.month, self.birth_day.day):
years -= 1
return years

def is_adult(self):
return self.age() >= 18


p1 = Person("Sara", date(1996, 10, 28), "macOS")
print(p1)

p2 = Person("Sara", date(1996, 10, 28), "macOS")
print(p1 == p2)
21 changes: 21 additions & 0 deletions Sprint5/double.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
def double(n):
return n*2
num = double("22")
print(num)

#my prediction is that it would return 2222. As python will treat 22 as a string being a strongly typed language.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically Python is "duck typed" not "strongly typed" - worth googling the difference.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting the answer that i got is that Python is a strongly typed, dynamically typed language that uses duck typing. I have understood how it can behave in both ways as types can be check at runtime and that behavior matters more than the declared type

#if it was javascript * operator would have forced numeric converion and 22 string would have become 22 int and 44 would
#have been returned
# when I run the file it returns 2222 as expected.

# 2nd exercise- original function :
def double(number):
return number * 3

print(double(10)) # returns "30"

#so we either change the name of the function to triple or multiply number with 2 rather than 3
def triple(number):
return number * 3

print(triple(10)) # returns "30"
93 changes: 93 additions & 0 deletions Sprint5/e-num.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from dataclasses import dataclass
from enum import Enum
from typing import List
import sys


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

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


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

#library of laptops
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),
]

def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]:
possible_laptops = []
for laptop in laptops:
if laptop.operating_system == person.preferred_os:
possible_laptops.append(laptop)
return possible_laptops # returns an array of possible laptops for the person

#accept userinput
name = input("Enter your name: ").strip()
age_str = input("Enter your age: ").strip() #input always returns a str, we need to convert this to an integer
os_str = input("Enter your preferred operating system: ").strip()
# Validate age
try:
age = int(age_str)
except ValueError:
print("Error: Age must be a number.", file= sys.stderr)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Liking the use of stderr here.

sys.exit(1)

# Validate OS
try:
preferred_os = OperatingSystem(os_str)
except ValueError:
print("Error: Invalid operating system.", file=sys.stderr)
sys.exit(1)

#Create a person after validation
person = Person(name = name, age = age, preferred_os =preferred_os)
#finding laptops for this person
possible = find_possible_laptops(laptops, person)
print(f"The laptop library has {len(possible)} with {preferred_os.value}.")

# Start with an empty dictionary
os_counts: dict[OperatingSystem, int] = {}

for laptop in laptops:
os = laptop.operating_system
# If we've seen this OS before, add 1, otherwise start at 1
if os in os_counts:
os_counts[os] += 1
else:
os_counts[os] = 1

best_os = None
best_count = -1

# Loop through each (key, value) pair
for pair in os_counts.items():
os = pair[0] # the key (operating system)
count = pair[1] # the value (number of laptops)

# Check if this count is bigger than the best so far
if count > best_count:
best_os = os
best_count = count

print("Best OS:", best_os, "with", best_count, "laptops")
if best_os != preferred_os:
print(f"If you’re willing to accept {best_os.value}, "
f"you’re more likely to get a laptop since there are {best_count} available.")
21 changes: 21 additions & 0 deletions Sprint5/generics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
name: str
age : int
children: list["Person"] #nside the Person class- the Person-type doesnt exist, so we add "" to person,



Muhib = Person(name="Muhib",age= 7, children=[])
Muiz = Person(name="Muiz",age= 4,children=[])

Sara = Person(name="Sara",age= 31, children=[Muhib, Muiz])

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

print_family_tree(Sara)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all good. If you want (and only if you want) a stretch objective, make it cope with children who have children and print out a proper tree (so children, grandchildren, great-grandchildren increasingly indented). Definitely an optional task

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it and It introduced me to the concept of recursion. I have never come across them before and really tried understanding it throught the family tree example.

40 changes: 40 additions & 0 deletions Sprint5/lastexer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Parent:
"""Represents a person with a first and last name."""
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 the full name as 'First Last'."""
return f"{self.first_name} {self.last_name}"


class Child(Parent):
"""Represents a person who can change last names, tracking previous ones."""
def __init__(self, first_name: str, last_name: str):
"""Initialize a Child with a first and last name, plus a list of previous last names."""
super().__init__(first_name, last_name)
self.previous_last_names: list[str] = []

def change_last_name(self, last_name) -> None:
"""Change the last name and record the previous one."""
self.previous_last_names.append(self.last_name)
self.last_name = last_name

def get_full_name(self) -> str:
"""Return the full name, with suffix showing original last name if changed."""
suffix = ""
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())
24 changes: 24 additions & 0 deletions Sprint5/methods.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import date
class Person:
def __init__(self, name: str, birth_day: date):
self.name = name
self.birth_day = birth_day

def age(self)-> int: #returns age in years
today = date.today()
years = today.year - self.birth_day.year
if(today.month, today.day) < (self.birth_day.month, self.birth_day.day):
years -= 1
return years

def is_adult(self):
return self.age() >= 18

def __str__(self) -> str:
return f"{self.name}, born {self.birth_day}, age {self.age()}"

p1 = Person("Sara", date(1996, 10, 28))
print(p1.is_adult()) # True

p2 = Person("Muhib", date(2018, 6, 22))
print(p2.is_adult()) # False
42 changes: 42 additions & 0 deletions Sprint5/refactor.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"]),
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}")
34 changes: 34 additions & 0 deletions Sprint5/type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

from typing import Dict
def open_account(balances: Dict[str,int], name:str, amount:int)->None: #dic type str and int--- returns none/figured after running --strict flag with mypy
#Adding a new account
balances[name] = amount

def sum_balances(accounts:Dict[str,int])-> int: #takes type dic[str, int}returns int
total = int = 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: #type int returns type str
#formatting pence as a string
if total_pence < 100:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the account has a negative balance?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function didn’t explicitly handle negative balances. I’ve now updated it so negative values are formatted consistently by applying the minus sign to the final £/p output.

return f"{total_pence}p"
pounds =(total_pence // 100) #floordivision always returns an int
pence = total_pence % 100
return f"£{pounds}.{pence:02d}"

balances:Dict[str,int] = { #dic type {str:int}
"Sima": 700,
"Linn": 545,
"Georg": 831,
}

open_account(balances,"Tobi", 913) # correct passing of the argumenst to functions with balances.
open_account(balances,"Olya", 713)

total_pence = sum_balances(balances) #returntype int
total_string = format_pence_as_string(total_pence) #returntype str and wrong function name

print(f"The bank accounts total {total_string}")