Week 1 was about getting things to work.
Week 2 was about getting things to work properly.
The difference is bigger than I expected. I spent six days going back to code I was already proud of and realising it could be cleaner, safer, and smarter. Functions, error handling, built-in modules, file I/O — this week the pieces stopped feeling like separate topics and started feeling like a toolkit.
Here's what happened.
Day 8 — Functions: One Job, One Function
The rule I was given for Day 8: every function does ONE thing.
That sounds obvious until you look at your old code and realise every function you wrote does about four things. I went back to the adventure game from Week 1 and refactored it properly — breaking out start_game(), print_story(), print_location(), move_player(), beast(), check_up(), pick_up(), and game_end() into eight focused functions.
But the real lesson hit me when I built the tip calculator from scratch with clean separation from the start:
def main():
#Ask user to input bill, tip, people
bill = input("Enter total bill: ").strip()
tip = input("How much tip would you like to give?: ").strip()
people = input("Enter number of people: ").strip()
per_person = calculate(bill, tip, people)
print(f"Each person must cobntribute {per_person}$.")
def calculate(bill, tip, people):
#Convert from str to int and calculate the split
if "$" in bill:
bill = bill.removesuffix("$")
bill = float(bill)
if "%" in tip:
tip = tip.removesuffix("%")
tip = int(tip) / 100
people = int(people)
return round((bill + (bill * tip)) / people, 2)
main()
main() talks to the user. calculate() does the math. Neither steps on the other's toes.
Two things I didn't expect to love here: removesuffix() — so you can type "$42" or just "42" and it handles both — and how clean return makes functions feel. You hand it data, it hands you an answer. No side effects, no surprises.
Day 9 — Parameters & Return Values
Day 9 was about really understanding what flows into and out of functions. The tip calculator covered this well — calculate() takes three parameters, does one job, and returns one value.
What clicked: functions are just machines. You feed them inputs, they produce an output. Once I started thinking that way, writing them felt much more intentional. Every parameter has a purpose. Every return value means something.
I spent extra time making calculate() handle edge cases:
- What if someone types
$42with a dollar sign? →removesuffix("$")strips it - What if they type
15%instead of15? →removesuffix("%")strips that too - Division by zero for people? →
int(people)would be 0 and the function would crash — something I noted to fix with error handling later
This kind of thinking — what could go wrong? — is new for me. Week 1 me just hoped the user typed the right thing.
Day 10 — Scope & Modules
Day 10 was scope — understanding what variables live where — and modules, which is how Python lets you split code across files.
The adventure game got its cleanest version yet. Better input validation was added so the game no longer silently accepts invalid room names:
def move_player():
global player_location
while True:
go = input("\nWhere would you like to go?\nbedroom\nkitchen\nliving room\n\n ")
if go not in locations:
print("Invalid location please choose from the list")
if player_location == go:
print(f"Already in {player_location}")
else:
player_location = go
print_location()
beast()
if not game_state:
break
check_up()
break
The retry logic in beast() also got cleaner:
def beast():
global player_location
global game_state
beast_location = random.choice(locations)
if beast_location == player_location:
print("you are Dead")
retry_input = input("Would you like to play again?(yes/no) ")
if retry_input != "yes":
game_state = False
player_location = "bedroom"
else:
print("growlssss.........The beast is nearby")
The big scope lesson: global variables are a trade-off. They're convenient for a small game, but they make bigger programs hard to reason about. I'm already starting to see why Week 3 will probably involve classes.
Day 11 — Error Handling: try/except
This was one of my favourite days. Before try/except, my programs crashed the moment a user did something unexpected. After it, they handle the unexpected and keep running.
I built a days-until calculator — it tells you how many days until any date you enter:
from datetime import date
#Take input from user for target
while True:
try:
user_input = input("Enter you target date in (dd/mm/yyyy): ")
day, month, year = user_input.split("/")
day = int(day)
month = int(month)
year = int(year)
except ValueError:
print("Enter the date, month, year in dd/mm/yyyy format")
else:
break
target = date(year, month, day)
now = date.today()
days_until = target - now
print(f"There are {days_until.days} days until {user_input}")
Three things that clicked here at once:
-
try/except ValueErrorcatches the crash when the user types something that can't be split or converted to int — instead of crashing, it loops and asks again -
elseon atryblock — I didn't know this existed. It runs only if no exception was raised. Perfect for thebreakthat exits the loop -
from datetime import date— my first real standard library module.date.today()and date arithmetic (target - now) feel almost magical
Type
"christmas"into my program and it says "Enter the date, month, year in dd/mm/yyyy format."
Week 1 me's program would have thrown a wall of red text.
Day 12 — Built-in Libraries: random, datetime, sys
Day 12 was about getting comfortable with Python's standard library. I built a dice roller that tracks your roll history and lets you view it mid-session:
from random import randint
import sys
previous_values = []
global value
def roll():
'''Checks the user input and then rolls the dice and stores the value in history'''
while True:
while True:
roll_dice = input("Roll the dice?(yes/ no) [press h to view history] ").strip().lower()
if roll_dice == "yes" or roll_dice == "no" or roll_dice == "h":
break
if roll_dice == "no":
sys.exit()
elif roll_dice == "h":
if previous_values:
check_history()
else:
print("There is no history")
else:
value = randint(1, 6)
print(value)
history(value)
def history(store):
previous_values.append(store)
def check_history():
print(previous_values)
#Calling the roll function
roll()
New things that appeared this week:
-
Docstrings —
'''Checks the user input...'''right under the function definition. First time I've written proper in-code documentation -
sys.exit()— a clean way to quit a program from inside a function, without needing a flag variable -
from random import randint— importing just the function I need instead of the whole module
Three focused functions, clear responsibilities, history that persists through the session. This felt like real program design, not just scripts.
Day 13 — The Week 2 Project: A Password Manager
The end-of-week project brought everything together: functions, file I/O, modules, error handling, and a Caesar cipher for encryption. This is the most complex thing I've built so far.
Features:
- 🔐 Store passwords encrypted to a file
- 🔍 Retrieve and decrypt them by site name
- 📁 Persists between sessions using
json+pathlib - 🔑 Caesar cipher encryption on every stored password
from pathlib import Path
import json
import random
import sys
global file
global key
key = random.randint(3, 25)
file = Path("passwords.txt")
usr_data = {}
#Take command from the user
def command():
while True:
com = input("Enter command(store/retrive/'q' to quit): ").strip().lower()
if com == "store" or com == "retrive" or com == "q":
break
if com == "store":
password_site()
elif com == "retrive":
retrive_password()
else:
sys.exit()
#Store the password
def password_site():
while True:
site_name = input("Enter site name: ")
passwrd = input("Enter password: ").lower()
usr_data[site_name] = cipher(passwrd, key)
while True:
retry = input("Do you want to store a new password(yes/no)? ").lower()
if retry == "yes" or retry == "no":
break
if retry == "no":
file.write_text(json.dumps(usr_data, indent = 4))
print("Saved to file..")
return
#retrive the password
def retrive_password():
contents = json.loads(file.read_text())
site = input("Enter site name to retrive your password: ")
if site in contents:
usr_password = decrypt(contents[site], key)
print(f"Your password for {site} is {usr_password}")
else:
print(f"Not found site name: {site}")
#Encrypting the password using ceasar cipher
def cipher(strng, k):
new_strng = ""
for char in strng:
if char.isalpha():
if char.islower():
c_num = ((ord(char) - ord("a")) % 26) + ord("a")
new_strng += chr(c_num)
elif char.isupper():
ch_num = ((ord(char) - ord("A")) % 26) + ord("A")
new_strng += chr(ch_num)
else:
new_strng += char
return new_strng
#Decrypting the password
def decrypt(pss, k):
new_pss = ""
for char in pss:
if char.isalpha():
if char.islower():
char_num = ((ord(char) - ord("a")) % 26) - ord("a")
new_pss += chr(char_num)
elif char.isupper():
chr_num = ((ord(char) - ord("A")) % 26) - ord("A")
new_pss += chr(chr_num)
else:
new_pss += char
return new_pss
command()
This week's project felt different from last week's. The adventure game was fun. This one felt useful. Real file persistence, real encryption logic, real user commands. I stored a password, closed the program, reopened it, retrieved the password — and it worked. That moment hit differently.
📁 What I Built This Week
| Project | File | Concepts Used |
|---|---|---|
| Tip Calculator | tip.py |
Functions, parameters, return values, removesuffix()
|
| Days-Until Calculator | days_until.py |
try/except, datetime, date arithmetic |
| Dice Roller with History | dice.py |
random, sys, docstrings, list history |
| Password Manager | passwords_manager.py |
File I/O, json, pathlib, Caesar cipher, all of the above |
| Adventure Game (refactored) | game.py |
Scope, input validation, cleaner function design |
All the code is on GitHub — Week 1 and Week 2, every file:
👉 github.com/Omk4314/progress-on-python
What Actually Clicked This Week
- One function, one job. Once I started following this rule, my code became easier to read, easier to fix, and easier to build on.
-
try/exceptchanges everything. Programs that handle bad input gracefully feel professional, not just functional. -
The standard library is enormous.
datetime,random,pathlib,json,sys— all built in, nothing to install. I've barely scratched the surface. - File I/O makes programs feel real. The moment my password manager wrote to a file and read it back, it stopped feeling like a script and started feeling like software.
- Refactoring is a skill. Going back to Week 1 code and improving it wasn't boring — it was genuinely satisfying.
What I Want to Learn Next
Week 3 is going to push further:
- Classes and OOP — I can feel the limits of global variables and I think objects are the answer
- More on file I/O — reading CSVs, writing logs
- Bigger projects — something that ties multiple files together properly
- Maybe some APIs — pulling real data into Python feels like the next unlock
If you're following along or learning Python yourself, drop a comment — I'd love to see what you're building too.
See you in Week 3. 🐍
Two weeks in. The programs are getting real. So is the learning.
Top comments (0)