Personally developed Python practice exercises.
I developed these exercises as part of my Python practice process. I designed these exercises independently, and used AI assistants for tutoring and review. I'd try the code myself, share my attempt, and learn througu feedback. I'd then practice the exercises in my terminal to build procedural memory and develop fluency.
cities["abbrev"]
Based on a dictionary of East Coast and West Coast cities, I abbreviate each city's name, depending on whether they contain one word or two. Starting with a dictionary of cities, create a function that updates cities["abbrev"] with esch city's acronym or abbreviation.
If a city name contains a space, returns an acronym of the first letter of each word. For single-word cities, use the first three letters and convert them to uppercase.
cities = {
"East Coast": ["New York", "Boston", "Washington DC"],
"West Coast": ["Los Angeles", "San Francisco", "Palo Alto"],
"abbrev": []
}
for coast in ["East Coast", "West Coast"]:
for city in cities[coast]:
if " " in city:
cities["abbrev"].append(
city[0].upper()
+ city[city.index(" ") + 1].upper()
)
else:
cities["abbrev"].append(city[:3].upper())
print(cities["abbrev"])Output:
['NY', 'BOS', 'WD', 'LA', 'SF', 'PA']Updated cities dict:
print(cities)
{'East Coast': ['New York', 'Boston', 'Washington DC'],
'West Coast': ['Los Angeles', 'San Francisco', 'Palo Alto'],
'abbrev': ['NY', 'BOS', 'WD', 'LA', 'SF', 'PA']}Note: This version uses the first two words of a city name with multiple words. For example, New York City becomes 'NY', not 'NYC'.
sun_times(day, anchors)
Estimate sunrise and sunset timing for any date by calculating the date's position between two of four anchor points.
We'll assume the dates of the equinoxes and solstices are March 21, June 21, September 21, and December 21.
from datetime import date
spring = (3, 21)
summer = (6, 21)
fall = (9, 21)
winter = (12, 21)
def hm(h, m=0):
return h * 60 + m
def sun_times(day, anchors):
"""
anchors format: {
spring: (sunrise_minutes, sunset_minutes),
summer: (sunrise_minutes, sunset_minutes),
fall: (sunrise_minutes, sunset_minutes),
winter: (sunrise_minutes, sunset_minutes)
}
Times are minutes after midnight.
Example:
6:30 AM: hm(6, 30) = 360, 6:15 PM: hm(18, 15) = 1095
"""
# Determine which pair of anchor dates surrounds the target date in order to obtain a date index.
month = day.month
d = day.day
if (month, d) >= spring and (month, d) < summer:
last_anchor = spring
next_anchor = summer
day_index = (day - date(day.year, *spring)).days
elif (month, d) >= summer and (month, d) < fall:
last_anchor = summer
next_anchor = fall
day_index = (day - date(day.year, *summer)).days
elif (month, d) >= fall and (month, d) < winter:
last_anchor = fall
next_anchor = winter
day_index = (day - date(day.year, *fall)).days
elif (month, d) >= winter:
last_anchor = winter
next_anchor = spring
day_index = (day - date(day.year, *winter)).days
else:
# Jan 1 through Mar 20
last_anchor = winter
next_anchor = spring
day_index = (
day - date(day.year - 1, *winter)
).days
last_sunrise, last_sunset = anchors[last_anchor]
next_sunrise, next_sunset = anchors[next_anchor]Apply a factor of 1/90 to the date index, and multiply by the difference between next and last anchor sunrise/sunset.
sunrise = (
last_sunrise
+ day_index * (1 / 90)
* (next_sunrise - last_sunrise)
)
sunset = (
last_sunset
+ day_index * (1 / 90)
* (next_sunset - last_sunset)
)
return sunrise, sunset
# alternatively, we can also define span = (date(day.year, *next_anchor) - date(day.year, *last_anchor)).daysExample:
anchors = {
spring: (hm(6, 0), hm(18, 0)), # 6:00 AM, 6:00 PM
summer: (hm(5, 0), hm(20, 30)), # 5:00 AM, 8:30 PM
fall: (hm(6, 0), hm(18, 0)), # 6:00 AM, 6:00 PM
winter: (hm(7, 0), hm(16, 40)), # 7:00 AM, 4:40 PM
}
sunrise, sunset = sun_times(
date(2026, 5, 5),
anchors)
print(sunrise)
print(sunset)A system for generating ID numbers containing 4 letters and 5 digits. Each ID is represented by a numbered key in a dictionary containing the current ID, prior IDs, and the characters changed in the most recent generation.
When a new ID is generated, 2 characters are replaced according the following rules.
Rules:
- Each ID must contain exactly 9 characters.
- Each ID must contain exactly 4 letters and 5 digits.
- Exactly 2 characters must be replaced per generation.
- The characters replaced in the previous generation are preserved.
- No previously used ID may be returned.
Replacement logic:
-
If a number or letter was replaced in the previous ID generation, preserve that character.
-
For remaining positions:
- If a number: Sum the digits of the product of the ID's numeric values, and add this number to the target digit.
- If a letter: Map the letter's position (A-Z) to a corresponding numerical value between 1-26. Add the product of the ID’s numeric values to the target letter, and re-assign to a new letter.
def assign_new_id(members, holder):
member = members[holder]
current = member["current_id"]
used = {
id_
for m in members.values()
for id_ in m["id_history"]
}
available = [
i for i in range(len(current))
if i not in member["last_changed"]
]
seed = 1
for c in current:
if c.isdigit():
seed *= int(c) + 1
while True:
first = available[seed % len(available)]
rest = [i for i in available if i != first]
second = rest[seed % len(rest)]
new = list(current)
for i in (first, second):
if current[i].isalpha():
new[i] = chr(65 + (seed + i) % 26)
if new[i] == current[i]:
new[i] = chr(65 + (seed + i + 1) % 26)
else:
new[i] = str((seed + i) % 10)
if new[i] == current[i]:
new[i] = str((seed + i + 1) % 10)
new = "".join(new)
if new not in used:
break
seed += 1
member["current_id"] = new
member["id_history"].append(new)
member["last_changed"] = [first, second]
return newExample:
# Sample members dataset
members = {
101: {
"current_id": 'ABCD12345',
"id_history": ['ABGH12345', 'ABCD12345',]
"last_changed": [2, 3]
},
}
# Run function for member 101
holder = 101
new_id = assign_new_id(members, holder)
print(f"Generated New ID: {new_id}")
print("Updated Member Data:")
print(members[holder])
# Output
Generated New ID: STCD12345
Updated Member Data:
{'current_id': 'STCD12345',
'id_history': ['ABGH12345', 'ABCD12345', 'STCD12345'],
'last_changed': [0, 1]}
# The function changes ABCD12345 to STCD12345, replacing A with S and B with T.I practiced Python during a visit to New York for a family event. A common topic at family gatherings concerns the boarding of everyone's pets, since only some relatives accept pets.
This exercise determines whose pet can tag along, and whose have to be boarded.
people = {
"Mom": {"rules": []},
"Jay": {"rules": []},
"Sue": {"rules": ["cat"]},
"Steven": {"rules": []},
"Shannon": {"rules": []},
"Arnold": {"rules": ["dog"]},
}
households = {
"Mom": {"residents": ["Mom"], "accepts_pets": True},
"Jay_and_Sue": {"residents": ["Jay", "Sue"], "accepts_pets": True},
"Steven": {"residents": ["Steven"], "accepts_pets": True},
"Shannon": {"residents": ["Shannon"], "accepts_pets": False},
"Arnold": {"residents": ["Arnold"], "accepts_pets": True},
}
pets = {
"Parker": {"kind": "dog", "owner": "Mom"},
"Mouse": {"kind": "cat", "owner": "Mom"},
"Corgy": {"kind": "dog", "owner": "Steven"},
"Wilson": {"kind": "dog", "owner": "Jay"},
"Collie": {"kind": "dog", "owner": "Shannon"},
}Now define households, house rules, and boarding, and format the results:
def household_of(person, households):
for house, info in households.items():
if person in info["residents"]:
return house
return None
def house_rules(house, households, people):
rule_against = []
for resident in households[house]["residents"]:
rule_against.extend(people[resident]["rules"])
return rule_against
def boarding(pets, visits, households, people):
result = {}
for pet, info in pets.items():
owner = info["owner"]
kind = info["kind"]
if owner not in visits:
result[pet] = ("Stays", f"since {owner} is not traveling")
continue
house = household_of(visits[owner], households)
if kind in house_rules(house, households, people):
result[pet] = (
"Boarded",
f"since {owner} is visiting {house}, who cannot have {kind}s")
elif households[house]["accepts_pets"]:
result[pet] = (
"Comes",
f"since {owner} is visiting {house}, who welcomes {kind}s")
else:
result[pet] = (
"Boarded",
f"since {owner} is visiting {house}, who does not take {kind}s")
return resultExample: Mom visiting Aunt Sue
visits = {"Mom": "Sue"}
for pet, (boards, why) in boarding(
pets, visits, households, people
).items():
print(pet, boards, why)
# Result:
Parker Comes since Mom is visiting Jay_and_Sue, who welcomes dogs
Mouse Boarded since Mom is visiting Jay_and_Sue, who cannot have cats
Corgy Stays since Steven is not traveling
Wilson Stays since Jay is not traveling
Collie Stays since Shannon is not travelingdef ratio_method(incident_count, population, total_damages, gdp_per_capita,
sv1: int, sv2: str, th: str, lc1: int, lc2: int):
return {
# Frequency (F): how often the harm occurs, incidents relative to
# the exposed population.
"Frequency": incident_count / population,
# Average Damages (AD): the typical size of harm per incident,
# compared against economic output (GDP per capita). Corresponds to what
# Gifford refers to as "severity."
"Average_Damages": total_damages / gdp_per_capita,
# Social Value 1 (SV1): the economic value that the activity adds —
# its productive benefit to society.
"SV1": int(sv1),
# Social Value 2 (SV2): the cultural inclination toward the
# activity — its value beyond pure economics.
"SV2": str(sv2),
# Type of Harm (TH): a classification across the six dimensions in
# TYPE_OF_HARM below (type of injury, defendant's role, physical
# agent, onset, location, reason). Every axis is categorical — none
# is quantitative.
"Type_Harm": str(th),
# Litigation Costs 1 (LC1): the cost structure of bringing a claim —
# information costs versus claim costs (Landes & Posner).
"LC1": int(lc1),
# Litigation Costs 2 (LC2): the difficulty
# of proving liability (Gifford factor 3).
"LC2": int(lc2)
# LC2 can be a string (e.g., "low", "moderate", "high") or an integer (e.g., 1-10), depending on how we define it.
}
# Type of Harm (TH): the article's "mechanisms of action" classification.
#Each harm is characterized across SIX dimensions, each with its own set of values.
# A real harm is one value drawn from each axis.
TYPE_OF_HARM = {
"type_of_injury": ["Physical/Medical", "Financial", "Environmental", "Rights Violation"],
"defendants_role": ["Direct Action", "Product Developer", "Failure of Oversight", "Vicarious/Passive"],
"physical_agent": ["Defendant", "Product/Device", "Third-Party(s)", "Non-Product Physical Object"],
"onset_of_injury": ["Immediate/Imminent", "Definite Future", "Prolonged", "Uncertain"],
"location_of_injury": ["Proximate Zone", "Spanning Zone", "Sporadic", "Intangible/Internet"],
"reason_for_injury": ["Malice", "Negligence", "Recklessness", "Pure Accident"],
}
## Thanks for reading!