first = "Python"
second = "Rocks"
print(f"{first} {second}!".upper())
You’ll see:
PYTHON ROCKS!
Takeaway: f‑strings let you write what you mean instead of how to stitch it together. They make your code clearer, shorter, and more Pythonic – exactly the vibe we love as beginners.
Happy coding, and may your strings always be f‑abulous! 🚀
❤1
🌟 Python tip for absolute beginners: How to loop over a list and know the position of each item using
When you write a
But sometimes you also need to know where that value sits in the list – its index (0 for the first item, 1 for the second, …).
The “old‑school” way (and why it’s clunky)
* Why it works:
* Why it feels awkward: you have to write the length of the list twice, you risk an “off‑by‑one” mistake, and the code mixes two different ideas (counting and accessing) in one line.
What’s happening?
*
* Each pair is a tuple: the first element is the current index, the second element is the original item.
* The
Because
Starting the count at a different number
Sometimes you want the numbering to start at 1 (or any other number).
Output:
Why it works: the optional
Quick checklist – when to use
- You need the position of each item (e.g., to label output, to compare with another list, or to skip a specific index).
- You want cleaner code that avoids manual
- You care about performance on large data –
Mini‑exercise
Try this on your own:
What do you see? Experiment by changing
Takeaway:
enumerate When you write a
for‑loop you usually care about the value you’re iterating over:fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
But sometimes you also need to know where that value sits in the list – its index (0 for the first item, 1 for the second, …).
The “old‑school” way (and why it’s clunky)
for i in range(len(fruits)):
print(i, fruits[i])
* Why it works:
range(len(fruits)) creates a sequence of numbers from 0 up to the length of the list minus one. Inside the loop you use that number (i) to fetch the item with fruits[i]. * Why it feels awkward: you have to write the length of the list twice, you risk an “off‑by‑one” mistake, and the code mixes two different ideas (counting and accessing) in one line.
enumerate to the rescuefor index, fruit in enumerate(fruits):
print(index, fruit)
What’s happening?
*
enumerate(fruits) takes the original list and produces a new sequence of pairs. * Each pair is a tuple: the first element is the current index, the second element is the original item.
* The
for loop automatically unpacks that tuple into the variables index and fruit.Because
enumerate builds the pairs on the fly, it never creates a whole extra list in memory – it’s efficient even for very long sequences.Starting the count at a different number
Sometimes you want the numbering to start at 1 (or any other number).
enumerate lets you set a custom start:for pos, fruit in enumerate(fruits, start=1):
print(f"{pos}. {fruit}")
Output:
1. apple
2. banana
3. cherry
Why it works: the optional
start argument tells enumerate what value to use for the first index. After that it simply adds 1 for each subsequent item.Quick checklist – when to use
enumerate- You need the position of each item (e.g., to label output, to compare with another list, or to skip a specific index).
- You want cleaner code that avoids manual
range(len(...)) gymnastics. - You care about performance on large data –
enumerate is lazy and memory‑friendly.Mini‑exercise
Try this on your own:
words = ["hello", "world", "python", "rocks"]
for i, w in enumerate(words, start=5):
print(f"Item {i}: {w.upper()}")
What do you see? Experiment by changing
start and by removing it entirely. Notice how the output changes and how the code stays readable.Takeaway:
enumerate is a tiny built‑in tool that makes looping with indices both clear and safe. Keep it in your toolbox – you’ll reach for it every time you need “the item and its place” together. 🚀❤1
🚀 One‑Sitting Python Project: “Smart Folder Organizer”
Ever looked at a messy Downloads folder and thought, “I wish my files would tidy themselves up”? In a single sitting you can build a tiny utility that scans any directory, decides where each file belongs (pictures, docs, archives, etc.) and moves it into a matching sub‑folder. The result is an instant declutter that you can run whenever you need – and the code itself is a great playground for the core ideas every new Python coder should master.
What you’ll learn (and why it matters)
Concept — Why it’s useful
**️⃣
🔀
📂 Dictionaries — Perfect for mapping file extensions (e.g.,
🔁 Loops & conditionals — The backbone of any automation – you’ll iterate over every file, check its type, and decide what to do.
⚠️ Basic error handling — Using
All of these are building blocks you’ll keep using in bigger projects, so mastering them now pays off later.
Quick sketch of the approach
1. Pick a folder – you can pass it as a command‑line argument or hard‑code a path for a quick test.
2. Define categories – a small dictionary where keys are tuples of extensions and values are the folder name you want them to end up in.
3. Iterate over every entry in the chosen directory with
4. Identify the file type by looking at
5. Create the target sub‑folder if it doesn’t already exist (
6. Move the file with
7. Handle exceptions so the script never crashes on a single problematic file.
That’s it – roughly 30‑40 lines of code, and you have a reusable tool you can run manually or schedule with a cron job / Windows Task Scheduler.
Minimal but complete code snippet
Why this works
(continued below 👇)
Ever looked at a messy Downloads folder and thought, “I wish my files would tidy themselves up”? In a single sitting you can build a tiny utility that scans any directory, decides where each file belongs (pictures, docs, archives, etc.) and moves it into a matching sub‑folder. The result is an instant declutter that you can run whenever you need – and the code itself is a great playground for the core ideas every new Python coder should master.
What you’ll learn (and why it matters)
Concept — Why it’s useful
**️⃣
pathlib.Path — Gives you an object‑oriented way to work with file system paths – clearer than the old os.path functions and works the same on Windows, macOS and Linux.🔀
shutil.move — Handles moving files across directories (even across different drives) with a single call; you don’t have to reinvent copy‑and‑delete logic.📂 Dictionaries — Perfect for mapping file extensions (e.g.,
.jpg) to destination folders (e.g., Images). This shows how data structures drive program flow.🔁 Loops & conditionals — The backbone of any automation – you’ll iterate over every file, check its type, and decide what to do.
⚠️ Basic error handling — Using
try/except protects you from permission errors or stray files that can’t be moved.All of these are building blocks you’ll keep using in bigger projects, so mastering them now pays off later.
Quick sketch of the approach
1. Pick a folder – you can pass it as a command‑line argument or hard‑code a path for a quick test.
2. Define categories – a small dictionary where keys are tuples of extensions and values are the folder name you want them to end up in.
3. Iterate over every entry in the chosen directory with
Path.iterdir(). 4. Identify the file type by looking at
Path.suffix.lower(). 5. Create the target sub‑folder if it doesn’t already exist (
Path.mkdir(exist_ok=True)). 6. Move the file with
shutil.move(). 7. Handle exceptions so the script never crashes on a single problematic file.
That’s it – roughly 30‑40 lines of code, and you have a reusable tool you can run manually or schedule with a cron job / Windows Task Scheduler.
Minimal but complete code snippet
import shutil
from pathlib import Path
# 1️⃣ Folder you want to clean up
SOURCE = Path.home() / "Downloads" # change to any folder you like
# 2️⃣ Mapping of extensions → destination folder name
CATEGORIES = {
("jpg", "jpeg", "png", "gif", "bmp"): "Images",
("pdf", "doc", "docx", "txt", "odt"): "Documents",
("mp3", "wav", "flac"): "Audio",
("mp4", "mov", "avi", "mkv"): "Videos",
("zip", "tar", "gz", "rar"): "Archives",
}
def get_category(ext: str) -> str:
"""Return the folder name for a given file extension, or 'Other'."""
for exts, folder in CATEGORIES.items():
if ext in exts:
return folder
return "Other"
def organize_folder(folder: Path) -> None:
"""Walk through *folder* and move each file into its category."""
if not folder.is_dir():
raise NotADirectoryError(f"{folder} is not a directory")
for item in folder.iterdir():
# Skip sub‑folders – we only want to move files
if item.is_dir():
continue
ext = item.suffix.lower().lstrip('.') # e.g. 'jpg'
target_folder = folder / get_category(ext)
target_folder.mkdir(exist_ok=True) # create if missing
try:
shutil.move(str(item), str(target_folder))
print(f"✅ Moved {item.name} → {target_folder.name}/")
except Exception as e:
print(f"⚠️ Could not move {item.name}: {e}")
if __name__ == "__main__":
organize_folder(SOURCE)
Why this works
(continued below 👇)
-
-
-
- The
How to take it further (optional fun)
- Add a date‑based rule: move files older than 30 days into an “Old” sub‑folder.
- GUI with
- Configuration file (
- Dry‑run mode: print the actions without actually moving anything – great for testing.
💡 TL;DR: In under an hour you’ll have a script that turns a chaotic download folder into a tidy hierarchy, while learning path handling, dictionary look‑ups, file operations, and basic error handling. Run it, watch the files shuffle into place, and feel the instant reward of automation. Happy coding!
Path.suffix gives the file extension including the leading dot (.pdf). Stripping the dot and lower‑casing makes the comparison case‑insensitive (PHOTO.JPG still matches “Images”). -
target_folder.mkdir(exist_ok=True) creates the destination only once; subsequent runs won’t raise an error. -
shutil.move automatically decides whether it can perform a simple rename (same drive) or must copy‑then‑delete (different drives). - The
try/except block catches any unexpected problem (locked file, permission denied) and lets the script continue processing the rest of the files.How to take it further (optional fun)
- Add a date‑based rule: move files older than 30 days into an “Old” sub‑folder.
- GUI with
tkinter: let the user pick the source folder via a dialog box. - Configuration file (
json or yaml) so you can tweak categories without touching code. - Dry‑run mode: print the actions without actually moving anything – great for testing.
💡 TL;DR: In under an hour you’ll have a script that turns a chaotic download folder into a tidy hierarchy, while learning path handling, dictionary look‑ups, file operations, and basic error handling. Run it, watch the files shuffle into place, and feel the instant reward of automation. Happy coding!
👋 Hey Python newcomers!
Ever wanted to build a sentence that talks back to the user, like “Hello Alice, you are 23 years old!” without a bunch of confusing plus‑signs?
🎯 What we’ll learn
f‑strings – a super‑simple way to insert variables (or even tiny calculations) straight into a piece of text.
🤔 Why do f‑strings work?
1. They’re just strings – a piece of text that Python knows how to print.
2. The
3. Python evaluates those expressions first, turns the result into text, and then stitches everything together into one final string.
Think of it like a magic template: you write the shape of the sentence once, and Python fills in the blanks each time you use it.
🛠️ The basic syntax
What happens here?
- The leading
-
-
- The whole thing becomes the single string
✨ A few handy tricks
- Do math inside the braces – no need for extra variables.
- Call functions – anything that returns a value works.
- Control formatting – you can tell Python how many decimal places you want, how wide a field should be, etc.
📌 Common pitfalls (and how to avoid them)
- Forgot the
Fix: add the
- Mismatched braces – every
- Using backslashes inside the braces – keep the expression simple; if it gets messy, compute it first in a separate variable.
🚀 Try it yourself right now!
What you’ll see:
-
-
- The final
👍 Bottom line:
Whenever you need to combine text with variable values (or quick calculations), reach for an f‑string. It makes your code cleaner, easier to read, and saves you from fiddling with the
Happy coding, and may your strings always be f‑abulous! 🎉
Ever wanted to build a sentence that talks back to the user, like “Hello Alice, you are 23 years old!” without a bunch of confusing plus‑signs?
🎯 What we’ll learn
f‑strings – a super‑simple way to insert variables (or even tiny calculations) straight into a piece of text.
🤔 Why do f‑strings work?
1. They’re just strings – a piece of text that Python knows how to print.
2. The
f before the quotes tells Python “look inside” – every part that sits between { and } is treated as a normal Python expression. 3. Python evaluates those expressions first, turns the result into text, and then stitches everything together into one final string.
Think of it like a magic template: you write the shape of the sentence once, and Python fills in the blanks each time you use it.
🛠️ The basic syntax
name = "Alice"
age = 23
message = f"Hello {name}, you are {age} years old!"
print(message)
What happens here?
- The leading
f tells Python to pay attention to the curly braces. -
{name} is replaced by the value of the variable name. -
{age} is replaced by the value of age. - The whole thing becomes the single string
"Hello Alice, you are 23 years old!", which print then shows on the screen.✨ A few handy tricks
- Do math inside the braces – no need for extra variables.
x = 5
print(f"The double of {x} is {x * 2}.")
- Call functions – anything that returns a value works.
def greet(who):
return f"Hey, {who}!"
print(f"{greet('Bob')} You have {len('Bob')} letters in your name.")
- Control formatting – you can tell Python how many decimal places you want, how wide a field should be, etc.
price = 7.5
print(f"The price is ${price:.2f}") # → The price is $7.50
📌 Common pitfalls (and how to avoid them)
- Forgot the
f? name = "Sam"
# Oops – missing the leading f
print("Hi {name}") # prints literally {name}
Fix: add the
f → print(f"Hi {name}").- Mismatched braces – every
{ needs a matching }. print(f"Value is {value") # SyntaxError- Using backslashes inside the braces – keep the expression simple; if it gets messy, compute it first in a separate variable.
🚀 Try it yourself right now!
# Ask the user for their favorite fruit and how many they ate today
fruit = input("What fruit do you like? ")
count = int(input("How many did you eat today? "))
print(f"You ate {count} {fruit}(s) today. Good job!")
What you’ll see:
-
input() grabs what the user types. -
int() turns the text “3” into the number 3 so we can treat it as a number later. - The final
print uses an f‑string to build a friendly sentence in one line.👍 Bottom line:
Whenever you need to combine text with variable values (or quick calculations), reach for an f‑string. It makes your code cleaner, easier to read, and saves you from fiddling with the
+ operator or the older format() method.Happy coding, and may your strings always be f‑abulous! 🎉
🚀 One‑Sitting Python Project: “Pomodoro Companion”
You’ve probably heard of the Pomodoro Technique: work 25 minutes → break 5 minutes, repeat, and after four cycles take a longer rest. It’s a proven way to keep focus, and a tiny script can turn your computer into a personal timer without any fancy UI.
What the script will do
1. Ask how many cycles you want (default 4).
2. Count down the work‑period (25 min) and the short break (5 min).
3. Play a short beep (or show a desktop notification) at every transition.
4. Log each start/stop time to a plain‑text file so you can look back on how many minutes you actually spent focused.
All of this fits into a single Python file and can be typed out in under 30 minutes – perfect for a beginner who already knows variables, loops, and functions.
Core ideas you’ll see in action
Concept — Why it matters
Functions (
Optional third‑party library (
> Tip: If you haven’t installed any external package before, just run
How it works – step by step (plain language)
1. Collect user input –
2. Define a tiny helper
3.
* It records the start time (
* Enters a
* When the countdown hits zero, it logs the end time, calls
4. Main loop – for each Pomodoro we:
* Call
* Call
* Increment a counter and optionally print a friendly “Cycle X complete!” message.
All timestamps are written to a file called
You can open that file later and see exactly how many focused minutes you accumulated.
Minimal code you can copy‑paste and run
(continued below 👇)
You’ve probably heard of the Pomodoro Technique: work 25 minutes → break 5 minutes, repeat, and after four cycles take a longer rest. It’s a proven way to keep focus, and a tiny script can turn your computer into a personal timer without any fancy UI.
What the script will do
1. Ask how many cycles you want (default 4).
2. Count down the work‑period (25 min) and the short break (5 min).
3. Play a short beep (or show a desktop notification) at every transition.
4. Log each start/stop time to a plain‑text file so you can look back on how many minutes you actually spent focused.
All of this fits into a single Python file and can be typed out in under 30 minutes – perfect for a beginner who already knows variables, loops, and functions.
Core ideas you’ll see in action
Concept — Why it matters
time.sleep() — Pauses execution for a given number of seconds – the simplest way to create a countdown.datetime.now() — Gives you the exact current timestamp; we use it to write human‑readable log entries.while loops — Repeats the work/break cycle until the desired number of Pomodoros is reached.Functions (
run_timer, beep) — Keep the code tidy and reusable; each function does one clear job.Optional third‑party library (
plyer) — Provides cross‑platform desktop notifications (works on Windows, macOS, Linux). If you prefer a simple beep, the standard library winsound (Windows) or os.system('afplay /path/to/sound.wav') (macOS) can be used instead.> Tip: If you haven’t installed any external package before, just run
pip install plyer. The rest of the script only uses the built‑in standard library.How it works – step by step (plain language)
1. Collect user input –
input() returns a string; int() converts it to a number we can count with. 2. Define a tiny helper
beep() – depending on the OS, we either call the built‑in winsound.Beep (Windows) or ask the OS to play a default sound file. If you installed plyer, we call notification.notify() instead, which pops a small banner on the screen. 3.
run_timer(name, seconds) does the heavy lifting: * It records the start time (
datetime.now()). * Enters a
while loop that keeps subtracting one second from the remaining time, printing the minutes:seconds left on the same line (end='\r' rewrites the line). * When the countdown hits zero, it logs the end time, calls
beep() to signal the transition, and returns. 4. Main loop – for each Pomodoro we:
* Call
run_timer('Work', 25*60). * Call
run_timer('Break', 5*60). * Increment a counter and optionally print a friendly “Cycle X complete!” message.
All timestamps are written to a file called
pomodoro_log.txt in the same folder, e.g.:2026-09-23 10:00:00 – Work started
2026-09-23 10:25:00 – Work ended
2026-09-23 10:25:00 – Break started
2026-09-23 10:30:00 – Break ended
You can open that file later and see exactly how many focused minutes you accumulated.
Minimal code you can copy‑paste and run
(continued below 👇)
import time
import sys
from datetime import datetime
# ---------- optional notification ----------
try:
from plyer import notification
def alert(title, message):
notification.notify(title=title, message=message, timeout=3)
except ImportError:
# fallback: simple console beep
def alert(title, message):
sys.stdout.write('\a') # terminal beep
sys.stdout.flush()
print(f'\n--- {title}: {message} ---')
# ------------------------------------------
LOG_FILE = "pomodoro_log.txt"
def log(msg: str):
"""Append a timestamped line to the log file."""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"{timestamp} – {msg}\n")
def run_timer(name: str, seconds: int):
"""Count down `seconds` while printing a live timer."""
log(f"{name} started")
end_time = time.time() + seconds
while True:
remaining = int(end_time - time.time())
if remaining <= 0:
break
mins, secs = divmod(remaining, 60)
# carriage return rewrites the same line
print(f"{name}: {mins:02d}:{secs:02d}", end='\r')
time.sleep(1)
print(f"{name}: 00:00 ")
log(f"{name} ended")
alert(name, "Time's up!")
def main():
try:
cycles = int(input("How many Pomodoros? (default 4) ").strip() or "4")
except ValueError:
cycles = 4
WORK_SEC = 25 * 60 # 25 minutes
BREAK_SEC = 5 * 60 # 5‑minute short break
for i in range(1, cycles + 1):
print(f"\n=== Pomodoro {i} of {cycles} ===")
run_timer("Work", WORK_SEC)
if i < cycles: # no break after the final cycle
run_timer("Break", BREAK_SEC)
print("\nAll done! 🎉 Check", LOG_FILE, "for your session summary.")
alert("Pomodoro", "All cycles completed!")
if __name__ == "__main__":
main()
Why this works
-
time.time() gives the current Unix timestamp (seconds since 1970). Adding the desired duration yields an absolute end moment, so the loop never drifts even if sleep(1) is a tiny bit off. -
print(..., end='\r') rewrites the same console line, creating a live countdown without flooding the terminal. -
with open(..., "a") opens the log file in append mode, meaning each run adds new lines instead of overwriting old data. -
try / except ImportError lets the script gracefully fall back to a simple beep if the optional plyer library isn’t present – a nice pattern for “optional features”. Ready, set, code!
Take the snippet, paste it into a file called
pomodoro.py, run `python pom🚀 One‑Sitting Python Project: “Mini‑Expense Tracker”
Ever wish you could just jot down what you spent on a coffee, a bus ticket or a new book without opening a spreadsheet? Build a tiny command‑line tool that saves every entry to a CSV file and can instantly show you today’s total, a category breakdown, or the whole history. You can finish it in a single sitting and keep extending it later.
Why this project works for beginners
* CSV = plain text – No database required. Python’s built‑in
*
*
*
All of these modules are part of Python’s standard library, so you don’t need to install anything extra.
The core idea in plain English
1. Add a record – The user supplies a category (e.g., “food”), an amount (e.g., 4.50), and an optional note. The script records the current date and writes a new line to
2. Show recent entries – When the user runs
3. Summarise –
Because the data lives in a CSV, you can open it later in Excel, Google Sheets, or any text editor.
What you’ll need to know (and a quick definition)
*
*
*
*
A tiny code sketch (just enough to get you started)
(continued below 👇)
Ever wish you could just jot down what you spent on a coffee, a bus ticket or a new book without opening a spreadsheet? Build a tiny command‑line tool that saves every entry to a CSV file and can instantly show you today’s total, a category breakdown, or the whole history. You can finish it in a single sitting and keep extending it later.
Why this project works for beginners
* CSV = plain text – No database required. Python’s built‑in
csv module lets you read and write rows as simple lists. *
pathlib for file paths – It gives you an object‑oriented way to point at “expenses.csv” that works on Windows, macOS and Linux alike. *
argparse for a friendly CLI – You can call the script like a tiny app: python expense.py add … or python expense.py summary. *
datetime for dates – Store dates in ISO‑format (YYYY‑MM‑DD) so they sort naturally and are easy to parse later. All of these modules are part of Python’s standard library, so you don’t need to install anything extra.
The core idea in plain English
1. Add a record – The user supplies a category (e.g., “food”), an amount (e.g., 4.50), and an optional note. The script records the current date and writes a new line to
expenses.csv. 2. Show recent entries – When the user runs
view, the script reads the file, turns each line into a dictionary, and prints a tidy list. 3. Summarise –
summary groups rows by category and adds the amounts, giving you a quick glance at where the money went.Because the data lives in a CSV, you can open it later in Excel, Google Sheets, or any text editor.
What you’ll need to know (and a quick definition)
*
csv.writer / csv.DictReader – Tools that convert Python objects to CSV rows and back. *
Path (from pathlib) – Represents a file path; you can call .exists() to check if the file is already there. *
argparse.ArgumentParser – Parses command‑line arguments like add, view, summary. Think of it as the “menu” of your app. *
defaultdict (from collections) – A dictionary that automatically creates a default value for a new key; perfect for summing amounts per category. A tiny code sketch (just enough to get you started)
(continued below 👇)
#!/usr/bin/env python3
import csv
import argparse
from pathlib import Path
from datetime import datetime
from collections import defaultdict
DATA_FILE = Path("expenses.csv")
def ensure_file():
"""Create the CSV with a header if it doesn't exist yet."""
if not DATA_FILE.exists():
with DATA_FILE.open("w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["date", "category", "amount", "note"])
def add_entry(args):
"""Append a new expense line."""
ensure_file()
today = datetime.now().date().isoformat() # e.g. '2026-09-24'
with DATA_FILE.open("a", newline="") as f:
writer = csv.writer(f)
writer.writerow([today, args.category, f"{args.amount:.2f}", args.note or ""])
print(f"✅ Added {args.amount:.2f} to {args.category}")
def view_entries(_):
"""Print every row nicely."""
if not DATA_FILE.exists():
print("📂 No data yet – add something first!")
return
with DATA_FILE.open() as f:
reader = csv.DictReader(f)
for row in reader:
print(f"{row['date']} | {row['category']:10} | ${row['amount']:>6} | {row['note']}")
def summary(_):
"""Show total spent per category."""
if not DATA_FILE.exists():
print("📂 No data yet – add something first!")
return
totals = defaultdict(float)
with DATA_FILE.open() as f:
for row in csv.DictReader(f):
totals[row["category"]] += float(row["amount"])
print("💰 Expense summary:")
for cat, amount in sorted(totals.items(), key=lambda x: -x[1]):
print(f" {cat:10}: ${amount:,.2f}")
def main():
parser = argparse.ArgumentParser(
description="A tiny one‑file expense tracker."
)
sub = parser.add_subparsers(dest="cmd", required=True)
# ---- add command ----
add = sub.add_parser("add", help="Add a new expense")
add.add_argument("category", help="e.g. food, transport, books")
add.add_argument("amount", type=float, help="Amount spent")
add.add_argument("-n", "--note", help="Optional short note")
add.set_defaults(func=add_entry)
# ---- view command ----
view = sub.add_parser("view", help="Show all entries")
view.set_defaults(func=view_entries)
# ---- summary command ----
summ = sub.add_parser("summary", help="Total per category")
summ.set_defaults(func=summary)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
How the code works (the “why”)
*
ensure_file() checks whether expenses.csv exists; if not, it creates the file once with a header line. This prevents “file not found” errors on the first run. *
add_entry() uses datetime.now().date().isoformat() – the ISO format sorts alphabetically exactly like chronological order, so later you can simply sort the file and the dates will line up. *
csv.writer writes a list of strings; f"{args.amount:.2f}" guarantees two decimal places, making the CSV tidy. *
view_entries() reads with csv.DictReader, which gives each row as a dictionary keyed by the header names, so you can refer to row["category"] instead of remembering column indexes. *
summary() accumulates amounts in a defaultdict(float). When a new category appears, the default value 0.0 is automatically created, letting you add directly with +=. The sorted(..., key=lambda x: -x[1]) orders categories by descending spend.Next‑step ideas (once you’ve got the basics working)
* Add a
--date flag so you can back‑date an expense. * Export a simple bar chart with
matplotlib (still a one‑liner). * Store the CSV in a hidden folder (
~/.expense_tracker/expenses.csv) using Path.home(). * Add a
delete sub‑command that removes a line by its row number. Give it a try, run the script a few times, and watch your own expense history grow
👋 Hey Python beginners!
Ever wanted to loop over a list and know which position each item is at?
You could count manually, but Python gives you a neat helper called
🎯 What
When you write a normal
you get each element, but you have no idea which index (0, 1, 2…) it came from.
1. the current index
2. the actual item
So the loop receives two values at once. This is why you often see
🛠️ How to use it (step‑by‑step)
1. Call
2. Optionally give a starting number – the default is
3. Unpack the two values inside the
What you’ll see
💡 Why it’s useful
- No manual counters – you don’t need a separate variable that you
- Less error‑prone – forgetting to increment or using the wrong variable is a common beginner mistake.
- Works with any iterable – not just lists. You can enumerate characters in a string, rows in a CSV reader, etc.
📚 Quick checklist
- Use
- Pick a start number that matches how you’ll show the result (
- Remember to unpack two variables (
Give it a try! Replace any loop where you’re manually tracking an index with
Happy coding! 🚀
Ever wanted to loop over a list and know which position each item is at?
You could count manually, but Python gives you a neat helper called
enumerate(). 🎯 What
enumerate() does When you write a normal
for loop like:for fruit in fruits:
print(fruit)
you get each element, but you have no idea which index (0, 1, 2…) it came from.
enumerate() wraps the original list and, on each step, produces a pair:1. the current index
2. the actual item
So the loop receives two values at once. This is why you often see
for i, item in … – i is the index, item is the value.🛠️ How to use it (step‑by‑step)
1. Call
enumerate() on any iterable (list, string, tuple, etc.). 2. Optionally give a starting number – the default is
0. 3. Unpack the two values inside the
for statement.fruits = ["apple", "banana", "cherry", "date"]
# start counting at 0 (default)
for index, fruit in enumerate(fruits):
print(f"Item #{index} is {fruit}")
print("---")
# start counting at 1 (more natural for humans)
for pos, fruit in enumerate(fruits, start=1):
print(f"Item #{pos} is {fruit}")
What you’ll see
Item #0 is apple
Item #1 is banana
Item #2 is cherry
Item #3 is date
---
Item #1 is apple
Item #2 is banana
Item #3 is cherry
Item #4 is date
💡 Why it’s useful
- No manual counters – you don’t need a separate variable that you
+= 1 each loop. - Less error‑prone – forgetting to increment or using the wrong variable is a common beginner mistake.
- Works with any iterable – not just lists. You can enumerate characters in a string, rows in a CSV reader, etc.
📚 Quick checklist
- Use
enumerate() when you need the position and the value. - Pick a start number that matches how you’ll show the result (
0 for programming, 1 for user‑facing lists). - Remember to unpack two variables (
index, item) inside the loop.Give it a try! Replace any loop where you’re manually tracking an index with
enumerate() and watch your code become cleaner and easier to read. Happy coding! 🚀
❤2
🚀 One‑Sitting Python Project: “Instant Currency Converter”
Ever opened a web page, typed a number, and wondered “what’s that in euros?” – only to jump to Google, type the conversion, and copy‑paste the result? Let’s eliminate that extra step with a tiny command‑line tool that talks to a free exchange‑rate API and gives you an instant answer. It can be written, tested, and run in a single sitting, even if you’ve only scratched the surface of Python.
🎯 What the script will do
1. Read three arguments from the terminal – the amount, the source currency code (e.g., USD), and the target currency code (e.g., EUR).
2. Call a public API (
3. Extract the needed rate, multiply it by the amount, and print a friendly message.
4. (Optional) Save the last 5 conversions to a tiny local text file so you have a quick history.
All of this can be done with the built‑in
🛠️ Core concepts you’ll see
Concept — Why it matters — Quick definition
Command‑line arguments (
HTTP GET request (
JSON parsing (
String interpolation (
File I/O (
Error handling (
📦 What you need to install
🧩 Sketch of the solution (no full tutorial – just the big picture)
1. Grab arguments –
2. Validate – make sure the amount is a number and the currency codes are three‑letter strings.
3. Build the API URL – the service expects something like
4. Send the request –
5. Decode JSON –
6. Print – use an f‑string:
7. Log – open a file
💡 Why it works
-
- The API returns JSON, which maps naturally onto Python dictionaries, so
-
- By wrapping the network call in
- Appending to a text file is the easiest form of persistence; you avoid dealing with databases while still getting a useful history.
(continued below 👇)
Ever opened a web page, typed a number, and wondered “what’s that in euros?” – only to jump to Google, type the conversion, and copy‑paste the result? Let’s eliminate that extra step with a tiny command‑line tool that talks to a free exchange‑rate API and gives you an instant answer. It can be written, tested, and run in a single sitting, even if you’ve only scratched the surface of Python.
🎯 What the script will do
1. Read three arguments from the terminal – the amount, the source currency code (e.g., USD), and the target currency code (e.g., EUR).
2. Call a public API (
https://api.exchangerate.host) that returns the latest rates in JSON format. 3. Extract the needed rate, multiply it by the amount, and print a friendly message.
4. (Optional) Save the last 5 conversions to a tiny local text file so you have a quick history.
All of this can be done with the built‑in
json and urllib modules, but we’ll use the very popular third‑party library requests because it makes HTTP calls readable and robust.🛠️ Core concepts you’ll see
Concept — Why it matters — Quick definition
Command‑line arguments (
sys.argv) — Lets the user control the script without opening the code. — A list that contains everything you typed after the script name.HTTP GET request (
requests.get) — Fetches data from the internet. — “Give me the resource at this URL, please.”JSON parsing (
response.json()) — Turns the raw text the API sends into Python objects (dicts, lists). — JSON = JavaScript Object Notation, a lightweight data‑exchange format.String interpolation (
f‑strings) — Makes output readable and dynamic. — f"{value}" inserts the value directly into the string.File I/O (
open(..., 'a')) — Stores a tiny history without any database. — “Append” mode adds to the end of a file.Error handling (
try/except) — Prevents the program from crashing on bad input or network hiccups. — Catches exceptions and lets you respond gracefully.📦 What you need to install
pip install requests
requests is pure Python, widely used, and works on every platform.🧩 Sketch of the solution (no full tutorial – just the big picture)
1. Grab arguments –
sys.argv[1:] gives you a list like ['100', 'USD', 'EUR']. 2. Validate – make sure the amount is a number and the currency codes are three‑letter strings.
3. Build the API URL – the service expects something like
https://api.exchangerate.host/convert?from=USD&to=EUR&amount=100. 4. Send the request –
requests.get(url) returns a Response object. 5. Decode JSON –
resp.json() gives a dict; the conversion result lives under ['result']. 6. Print – use an f‑string:
print(f"💰 100 USD = 92.34 EUR"). 7. Log – open a file
history.txt in append mode and write the same line with a timestamp.💡 Why it works
-
requests.get handles the whole HTTP handshake for you – DNS lookup, TCP connection, sending headers, receiving the response, and closing the socket. - The API returns JSON, which maps naturally onto Python dictionaries, so
resp.json()['result'] is just a number you can multiply or display. -
sys.argv is already populated by Python before your script runs, so you don’t need any extra parsing library for a simple three‑parameter tool. - By wrapping the network call in
try/except, you catch problems like “no internet” or “invalid currency code” and can show a helpful message instead of a cryptic traceback. - Appending to a text file is the easiest form of persistence; you avoid dealing with databases while still getting a useful history.
(continued below 👇)
🖥️ Minimal working example (copy‑paste and run!)
How to run it
You should see something like:
```
💱
#!/usr/bin/env python3
import sys
import requests
from datetime import datetime
API_URL = "https://api.exchangerate.host/convert"
def usage():
print("Usage: python conv.py <amount> <FROM> <TO>")
print("Example: python conv.py 250 USD EUR")
sys.exit(1)
def main():
# 1️⃣ Grab and validate arguments
if len(sys.argv) != 4:
usage()
amount_str, src, dst = sys.argv[1:]
try:
amount = float(amount_str)
except ValueError:
print("❗ Amount must be a number.")
usage()
src, dst = src.upper(), dst.upper()
if not (len(src) == len(dst) == 3):
print("❗ Currency codes must be three letters (e.g., USD, EUR).")
usage()
# 2️⃣ Build request URL
params = {"from": src, "to": dst, "amount": amount}
try:
resp = requests.get(API_URL, params=params, timeout=5)
resp.raise_for_status() # raise if HTTP error
data = resp.json()
except requests.RequestException as e:
print(f"❌ Network problem: {e}")
sys.exit(1)
# 3️⃣ Extract result
if not data.get("success"):
print("❗ Conversion failed – maybe an unknown currency?")
sys.exit(1)
converted = data["result"]
# 4️⃣ Show friendly output
now = datetime.now().strftime("%Y-%m-%d %H:%M")
line = f"{now} | {amount:.2f} {src} = {converted:.2f} {dst}"
print("💱", line)
# 5️⃣ Persist a short history (last 5 lines will be kept manually later)
with open("history.txt", "a", encoding="utf-8") as f:
f.write(line + "\n")
if __name__ == "__main__":
main()
How to run it
python conv.py 73.5 GBP USD
You should see something like:
```
💱
🚀 One‑Sitting Python Project: “Instant Image Resizer”
Ever opened a folder of photos only to discover they’re all massive, slowing down your phone or web uploads? A tiny script that shrinks any image to a user‑defined width (while keeping the original aspect ratio) can save you minutes — and it only takes ~30 minutes to code, even if you’re still fresh on Python.
What you’ll build
A command‑line tool that:
1. Accepts a path to a single image or a whole folder.
2. Rescales each picture so its longest side matches a width you choose (e.g., 800 px).
3. Saves the resized version next to the original, appending “_small” to the filename.
The result is a set of lightweight copies you can share instantly, while the originals stay untouched.
Why it works – the building blocks
Concept — What it does — Why it matters
Aspect‑ratio math —
All of these are either in the standard library (
Sketch of the workflow
1. Parse arguments – decide where to look and how big the output should be.
2. Collect files – if a directory is given, iterate over every
3. Open each image –
4. Compute the new size – keep the longer side at the target width, scale the other side proportionally.
5. Resize –
6. Save – construct a new filename (
Because each step is a single, well‑named function, the whole script stays under 100 lines and is easy to read.
A minimal but complete example
(continued below 👇)
Ever opened a folder of photos only to discover they’re all massive, slowing down your phone or web uploads? A tiny script that shrinks any image to a user‑defined width (while keeping the original aspect ratio) can save you minutes — and it only takes ~30 minutes to code, even if you’re still fresh on Python.
What you’ll build
A command‑line tool that:
1. Accepts a path to a single image or a whole folder.
2. Rescales each picture so its longest side matches a width you choose (e.g., 800 px).
3. Saves the resized version next to the original, appending “_small” to the filename.
The result is a set of lightweight copies you can share instantly, while the originals stay untouched.
Why it works – the building blocks
Concept — What it does — Why it matters
Pillow (PIL) — A friendly wrapper around low‑level image libraries. It can open JPEG/PNG, read dimensions, and write new files. — Handles the heavy lifting of decoding/encoding images so you don’t have to.pathlib — Provides an object‑oriented way to work with file system paths (Path). — Makes code readable (folder.iterdir()) and works on Windows/macOS/Linux without manual slash handling.argparse — Parses command‑line arguments (--size, --source). — Lets users run the script like a real utility (python resize.py pictures/ --size 800).Aspect‑ratio math —
new_height = int(original_height * new_width / original_width). — Guarantees the picture isn’t stretched or squashed – it just scales proportionally.if __name__ == "__main__": — Makes the file runnable as a script and importable as a module. — Good practice; you can later reuse the resize_image function in other projects.All of these are either in the standard library (
pathlib, argparse) or a single extra package (Pillow), which you can install with:pip install pillow
Sketch of the workflow
1. Parse arguments – decide where to look and how big the output should be.
2. Collect files – if a directory is given, iterate over every
*.jpg/*.png. 3. Open each image –
Image.open(path) gives you an object exposing .size. 4. Compute the new size – keep the longer side at the target width, scale the other side proportionally.
5. Resize –
img.resize((new_w, new_h), Image.LANCZOS) (LANCZOS = high‑quality down‑sampling). 6. Save – construct a new filename (
original_stem + "_small" + original_suffix) and write the file. Because each step is a single, well‑named function, the whole script stays under 100 lines and is easy to read.
A minimal but complete example
(continued below 👇)
#!/usr/bin/env python3
# resize.py – shrink images while preserving aspect ratio
import argparse
from pathlib import Path
from PIL import Image
def resize_image(src: Path, max_side: int) -> Path:
"""Resize *src* so its longest side == max_side. Return path of new file."""
with Image.open(src) as img:
orig_w, orig_h = img.size
# Determine scaling factor: 1 means no change, <1 means shrink
scale = max_side / max(orig_w, orig_h)
if scale >= 1: # Image already small enough
return src
new_w = int(orig_w * scale)
new_h = int(orig_h * scale)
# LANCZOS gives the best quality for down‑sampling
resized = img.resize((new_w, new_h), Image.LANCZOS)
# Build output name: picture_small.jpg
out_path = src.with_name(f"{src.stem}_small{src.suffix}")
resized.save(out_path, quality=85) # quality=85 keeps good visual quality
return out_path
def collect_images(root: Path):
"""Yield all .jpg/.jpeg/.png files under *root* (non‑recursive)."""
for p in root.iterdir():
if p.suffix.lower() in {".jpg", ".jpeg", ".png"} and p.is_file():
yield p
def main():
parser = argparse.ArgumentParser(
description="Resize one image or every image in a folder."
)
parser.add_argument(
"source",
type=Path,
help="Path to an image file or a folder containing images.",
)
parser.add_argument(
"--size",
type=int,
default=800,
help="Maximum width or height of the resized image (default: 800).",
)
args = parser.parse_args()
if args.source.is_file():
out = resize_image(args.source, args.size)
print(f"✔︎ Saved → {out}")
elif args.source.is_dir():
for img_path in collect_images(args.source):
out = resize_image(img_path, args.size)
print(f"✔︎ {img_path.name} → {out.name}")
else:
parser.error("Source must be a file or an existing directory.")
if __name__ == "__main__":
main()
How the code works
*
argparse turns a command like python resize.py photos/ --size 600 into a Path object (args.source) and an integer (args.size). *
Path.is_file() / Path.is_dir() decide which branch to follow – a single image or a batch. *
Image.open() lazily loads the picture; the with block ensures the file is closed promptly. * The scale factor (
max_side / max(orig_w, orig_h)) tells us how much to shrink: if the longest side is already ≤ max_side, we skip processing. *
img.resize(..., Image.LANCZOS) does the heavy math; LANCZOS is a high‑quality filter that avoids jagged edges. *
with_name() builds a new filename without touching the original; the script writes the new file beside the old one, so you can always roll back.**Next steps you can try on
👋 Hey Python explorers!
Ever stared at a list of numbers and wished you could quickly create a new list where each number is doubled?
Let’s unlock a neat little trick called list comprehensions. It’s like a mini‑factory that builds a new list for you in a single line, and it works by combining a loop and an expression.
🧩 What’s actually happening?
1. The loop part – goes through each element of an existing iterable (like a list).
2. The expression part – says “for each element, do this calculation and keep the result.”
Python takes those two pieces and sprinkles them together inside square brackets
> Why it works:
> In Python, everything that can be iterated over (lists, strings, ranges…) can be fed to a
✨ Simple example – doubling numbers
What changed?
- The
-
- Wrapping it all in
🎯 More practical uses
🔹 Filtering while you build – keep only the even numbers:
🔹 Working with strings – make a list of the first letters of each word:
🔹 Nested loops – create a multiplication table (2‑by‑3 grid) in one line:
📚 Tips for when to use (or not use) them
- 👍 Use when the transformation is short and the intent stays clear.
- ⚠️ Avoid when the expression gets long or you need many
- Remember: readability > cleverness. If a teammate (or future you) squints at the line, break it into a normal loop.
🚀 Quick challenge
Try turning this block into a list comprehension yourself:
(Hint: you’ll need both an expression and an if filter inside the brackets.)
💡 Bottom line: List comprehensions give you a concise, Pythonic way to build new lists while looping, filtering, and transforming data—all in one readable line. Once you get comfortable, you’ll find yourself reaching for them every time you need a quick list makeover. Happy coding! 🌟
Ever stared at a list of numbers and wished you could quickly create a new list where each number is doubled?
Let’s unlock a neat little trick called list comprehensions. It’s like a mini‑factory that builds a new list for you in a single line, and it works by combining a loop and an expression.
🧩 What’s actually happening?
1. The loop part – goes through each element of an existing iterable (like a list).
2. The expression part – says “for each element, do this calculation and keep the result.”
Python takes those two pieces and sprinkles them together inside square brackets
[]. The result is a brand‑new list, built element by element.> Why it works:
> In Python, everything that can be iterated over (lists, strings, ranges…) can be fed to a
for loop. A list comprehension is just a compact way to write that loop and the value you want to store, without needing an extra temporary list or a separate append call.✨ Simple example – doubling numbers
# The “old‑school” way with a loop
numbers = [1, 2, 3, 4, 5]
doubled = []
for n in numbers:
doubled.append(n * 2)
print(doubled) # → [2, 4, 6, 8, 10]
# The same thing with a list comprehension
doubled_comp = [n * 2 for n in numbers]
print(doubled_comp) # → [2, 4, 6, 8, 10]
What changed?
- The
for n in numbers part is the loop. -
n * 2 is the expression that tells Python what to keep. - Wrapping it all in
[] tells Python “make a list out of the results”.🎯 More practical uses
🔹 Filtering while you build – keep only the even numbers:
evens = [n for n in numbers if n % 2 == 0]
print(evens) # → [2, 4]
🔹 Working with strings – make a list of the first letters of each word:
sentence = "Python is fun and friendly"
first_letters = [word[0] for word in sentence.split()]
print(first_letters) # → ['P', 'i', 'f', 'a', 'f']
🔹 Nested loops – create a multiplication table (2‑by‑3 grid) in one line:
table = [[i * j for j in range(1, 4)] for i in range(1, 3)]
print(table) # → [[1, 2, 3], [2, 4, 6]]
📚 Tips for when to use (or not use) them
- 👍 Use when the transformation is short and the intent stays clear.
- ⚠️ Avoid when the expression gets long or you need many
if/elif/else branches – a regular for loop is easier to read. - Remember: readability > cleverness. If a teammate (or future you) squints at the line, break it into a normal loop.
🚀 Quick challenge
Try turning this block into a list comprehension yourself:
words = ["apple", "banana", "cherry", "date"]
long_words = []
for w in words:
if len(w) > 5:
long_words.append(w.upper())
print(long_words) # Expected: ['BANANA', 'CHERRY']
(Hint: you’ll need both an expression and an if filter inside the brackets.)
💡 Bottom line: List comprehensions give you a concise, Pythonic way to build new lists while looping, filtering, and transforming data—all in one readable line. Once you get comfortable, you’ll find yourself reaching for them every time you need a quick list makeover. Happy coding! 🌟
❤1
⚡️ Quick‑fire Python project: a “One‑Click QR Code Generator”
Ever needed a QR code for a link, Wi‑Fi password, or a simple note, but didn’t want to open a web service? In ~30 minutes you can build a tiny script that takes any text you type, turns it into a QR image, and saves it to your desktop. It’s a perfect mix of user input, external libraries, file I/O, and a glimpse of how data → image pipelines work.
Why it’s a good first‑project
* Instant feedback – open the generated PNG and scan it with your phone; you see the result immediately.
* Learn to install & import third‑party packages (
* Practice functions and error handling – keep the code tidy and robust.
* Touch a little bit of binary data – the library turns a string into a matrix of black/white squares and writes a proper PNG file for you, so you don’t have to deal with the low‑level image format yourself.
What you’ll need (all come with the standard Python distribution except the QR library)
*
*
*
If you haven’t installed the library yet, run:
Step‑by‑step outline (in plain English)
1. Ask the user for the text they want to encode.
2. Create a QRCode object – you can tweak version, error correction, box size, and border.
3. Add the data (the user’s text) to the object and ask it to fit the optimal size.
4. Render the image (
5. Print a friendly message with the file location.
Each of those steps corresponds to a single line or two of Python, so the whole script stays under 30 lines.
The code (copy‑paste, run, and watch the magic happen)
What’s happening under the hood?
(continued below 👇)
Ever needed a QR code for a link, Wi‑Fi password, or a simple note, but didn’t want to open a web service? In ~30 minutes you can build a tiny script that takes any text you type, turns it into a QR image, and saves it to your desktop. It’s a perfect mix of user input, external libraries, file I/O, and a glimpse of how data → image pipelines work.
Why it’s a good first‑project
* Instant feedback – open the generated PNG and scan it with your phone; you see the result immediately.
* Learn to install & import third‑party packages (
pip install qrcode[pil]). * Practice functions and error handling – keep the code tidy and robust.
* Touch a little bit of binary data – the library turns a string into a matrix of black/white squares and writes a proper PNG file for you, so you don’t have to deal with the low‑level image format yourself.
What you’ll need (all come with the standard Python distribution except the QR library)
*
qrcode – the high‑level wrapper that builds the QR matrix. *
Pillow (installed automatically as a dependency of qrcode[pil]) – handles saving the matrix as a PNG. *
os – to build a safe file path (optional but good practice). If you haven’t installed the library yet, run:
pip install qrcode[pil]
Step‑by‑step outline (in plain English)
1. Ask the user for the text they want to encode.
2. Create a QRCode object – you can tweak version, error correction, box size, and border.
3. Add the data (the user’s text) to the object and ask it to fit the optimal size.
4. Render the image (
make_image) and save it to a file (.png). 5. Print a friendly message with the file location.
Each of those steps corresponds to a single line or two of Python, so the whole script stays under 30 lines.
The code (copy‑paste, run, and watch the magic happen)
import os
import qrcode
def generate_qr(data: str, out_path: str) -> None:
"""
Turn *data* into a QR code and write it to *out_path*.
"""
# 1️⃣ Create a QRCode object – the parameters control size & resilience.
qr = qrcode.QRCode(
version=None, # let the library choose the smallest version
error_correction=qrcode.constants.ERROR_CORRECT_M, # ~15% error recovery
box_size=10, # how many pixels each “dot” occupies
border=4, # blank margin around the code
)
# 2️⃣ Feed the data into the QR builder.
qr.add_data(data)
qr.make(fit=True) # automatically adjust the matrix size
# 3️⃣ Render the matrix as an image (Pillow handles the PNG format).
img = qr.make_image(fill_color="black", back_color="white")
img.save(out_path)
def main() -> None:
# 📥 Grab what the user wants to encode.
user_input = input("🖊️ Enter the text/URL you want as a QR code: ").strip()
if not user_input:
print("❗️ Nothing entered – aborting.")
return
# 📂 Choose a safe place to store the file.
filename = "my_qr.png"
out_file = os.path.join(os.getcwd(), filename)
# 🚀 Generate and save.
generate_qr(user_input, out_file)
print(f"✅ QR code saved as {out_file}")
print("📱 Scan it with your phone to verify!")
if __name__ == "__main__":
main()
What’s happening under the hood?
(continued below 👇)
*
*
*
*
Next‑step ideas (optional challenges)
* Add a command‑line argument (
* Create a small GUI with
* Overlay a tiny logo in the centre (you’ll work with Pillow’s image‑paste feature).
Give it a try, snap a picture of the QR with your phone, and you’ll have a handy tool you built yourself. Happy coding! 🚀
qrcode.QRCode builds a matrix (a grid of 0/1) that represents the encoded data according to the QR standard. *
error_correction tells the encoder how much redundancy to add – higher levels let the code survive scratches or low‑light scans, at the cost of a bigger image. *
make(fit=True) asks the library to calculate the smallest version that can hold your data, so you don’t need to guess the size. *
make_image converts that binary matrix into an actual picture using Pillow; you never manipulate pixels yourself, but you could if you wanted to add a logo later. Next‑step ideas (optional challenges)
* Add a command‑line argument (
argparse) so you can run the script like python qrgen.py "https://…". * Create a small GUI with
tkinter to let users pick a file name via a dialog. * Overlay a tiny logo in the centre (you’ll work with Pillow’s image‑paste feature).
Give it a try, snap a picture of the QR with your phone, and you’ll have a handy tool you built yourself. Happy coding! 🚀
❤1
🐍 Python Tip for Newbies – Getting the Position of Items with
When you first start writing loops you’ll often see code like this:
It works, but it feels a bit clunky, right?
You’re asking Python for the length of the list, then turning the numbers 0…N‑1 into an index, and finally pulling the element out of the list again.
✨ Why
It gives you two things at once – the current position (index) and the value* at that position.
* It’s safer – you don’t have to remember to write
* It reads like plain English – “enumerate the list”, i.e. “go through the list while counting”.
Under the hood,
A tuple is just a short way to store a fixed‑size collection of items together. When you write
Python unpacks that tuple automatically, putting the first element into
📚 Simple example – printing a shopping list with numbers
What happens step‑by‑step
1.
2. The
3. The
Output
Notice how we never had to write
🛠️ When to use it
✅ Good for — ❌ Not needed when
You need both the index and the item (e.g., showing a numbered list, tracking progress). — You only care about the item itself (just
You want a clear, readable loop that won’t break if the list changes size. — You need to modify the list while looping (use a different approach).
🎯 Quick exercise
Try modifying the code to skip the first item but still start counting at 1:
What do you think the output will be? Give it a go in your Python REPL – the answer is just the last three items, numbered 1‑3.
💡 Takeaway:
enumerate() When you first start writing loops you’ll often see code like this:
for i in range(len(colors)):
print(i, colors[i])
It works, but it feels a bit clunky, right?
You’re asking Python for the length of the list, then turning the numbers 0…N‑1 into an index, and finally pulling the element out of the list again.
✨ Why
enumerate() is nicer It gives you two things at once – the current position (index) and the value* at that position.
* It’s safer – you don’t have to remember to write
len(my_list) correctly; Python handles it for you. * It reads like plain English – “enumerate the list”, i.e. “go through the list while counting”.
Under the hood,
enumerate() creates an iterator that, on each step, produces a tuple like (0, first_item), (1, second_item), … . A tuple is just a short way to store a fixed‑size collection of items together. When you write
for index, item in enumerate(my_list):
Python unpacks that tuple automatically, putting the first element into
index and the second into item. No extra indexing needed!📚 Simple example – printing a shopping list with numbers
shopping = ["apples", "bread", "milk", "chocolate"]
for position, product in enumerate(shopping, start=1):
# `start=1` makes the count begin at 1 instead of the default 0
print(f"{position}. {product}")
What happens step‑by‑step
1.
enumerate(shopping, start=1) creates an iterator that will give (1, "apples"), (2, "bread"), (3, "milk"), (4, "chocolate").2. The
for loop takes each tuple, splits it into position and product.3. The
print line uses an f‑string (the f before the quotes) to insert the variables directly into the text.Output
1. apples
2. bread
3. milk
4. chocolate
Notice how we never had to write
shopping[0], shopping[1], … – enumerate did the counting for us.🛠️ When to use it
✅ Good for — ❌ Not needed when
You need both the index and the item (e.g., showing a numbered list, tracking progress). — You only care about the item itself (just
for item in my_list:).You want a clear, readable loop that won’t break if the list changes size. — You need to modify the list while looping (use a different approach).
🎯 Quick exercise
Try modifying the code to skip the first item but still start counting at 1:
for pos, prod in enumerate(shopping[1:], start=1):
print(f"{pos}. {prod}")
What do you think the output will be? Give it a go in your Python REPL – the answer is just the last three items, numbered 1‑3.
💡 Takeaway:
enumerate() is a tiny helper that makes loops easier to read and less error‑prone. Whenever you find yourself writing range(len(...)) just to get an index, pause and replace it with enumerate(). Your future self (and anyone reading your code) will thank you!🚀 One‑Sitting Python Project: “Instant URL Shortener”
Ever copy a crazy‑long link, paste it in a chat, and watch it wrap around the screen? In under an hour you can build a tiny command‑line tool that takes any URL, shrinks it to a few characters, and even copies the result to your clipboard. No web server, no database – just pure Python and two very friendly libraries.
What you’ll learn (in plain English)
*
*
*
* JSON decoding – the API replies with a tiny JSON payload;
All of these concepts are “beginner‑level” – you’ll see them in action without any advanced magic.
How it works – step by step (conceptual)
1. Parse the input –
2. Call a public shortening service – TinyURL (or is.gd) offers a free HTTP endpoint that accepts a long URL and returns a short one.
3. Read the response – The service sends back JSON like
4. Show & copy – Print the short link to the terminal. If
That’s it – a complete, useful utility that you can run from any terminal.
Quick “starter code” (copy‑paste, then run
Why this works
(continued below 👇)
Ever copy a crazy‑long link, paste it in a chat, and watch it wrap around the screen? In under an hour you can build a tiny command‑line tool that takes any URL, shrinks it to a few characters, and even copies the result to your clipboard. No web server, no database – just pure Python and two very friendly libraries.
What you’ll learn (in plain English)
*
requests – a human‑friendly way to talk to other websites. It handles the whole HTTP ceremony (sending a GET/POST, reading the response) without you having to build headers yourself. *
argparse – the built‑in module that turns command‑line words into nice Python variables, so you can run python shortener.py https://example.com. *
pyperclip (optional but fun) – a cross‑platform helper that puts text straight into the system clipboard, so you can Ctrl+V the short link instantly. * JSON decoding – the API replies with a tiny JSON payload;
response.json() turns that into a Python dictionary you can read like a normal object. All of these concepts are “beginner‑level” – you’ll see them in action without any advanced magic.
How it works – step by step (conceptual)
1. Parse the input –
argparse reads the URL you typed after the script name. 2. Call a public shortening service – TinyURL (or is.gd) offers a free HTTP endpoint that accepts a long URL and returns a short one.
3. Read the response – The service sends back JSON like
{"result_url": "https://tinyurl.com/abc123"}. requests turns that into a Python dict. 4. Show & copy – Print the short link to the terminal. If
pyperclip is installed, automatically copy it so you can paste anywhere. That’s it – a complete, useful utility that you can run from any terminal.
Quick “starter code” (copy‑paste, then run
python shortener.py <your‑link>)#!/usr/bin/env python3
"""Instant URL Shortener – one‑sitting Python project."""
import argparse
import sys
try:
import requests # pip install requests
except ImportError:
sys.exit("❗ Install requests first: pip install requests")
# pyperclip is optional – we’ll just skip copying if it’s missing.
try:
import pyperclip # pip install pyperclip
CAN_COPY = True
except ImportError:
CAN_COPY = False
API_ENDPOINT = "https://api.tinyurl.com/create" # TinyURL's official API (requires API key)
# For a truly zero‑setup version you can use is.gd:
# API_ENDPOINT = "https://is.gd/create.php"
def shorten(url: str) -> str:
"""
Send *url* to the shortening service and return the short link.
"""
# Using is.gd because it needs no API key – just a GET request.
resp = requests.get(
"https://is.gd/create.php",
params={"format": "json", "url": url},
timeout=10
)
resp.raise_for_status() # raise an exception if the server responded with an error
data = resp.json()
# is.gd returns {"shorturl": "...", "errorcode": 0, ...}
if data.get("errorcode"):
raise RuntimeError(f"❗ Service error: {data.get('errormessage')}")
return data["shorturl"]
def main() -> None:
parser = argparse.ArgumentParser(
description="Turn any long URL into a short one in one command."
)
parser.add_argument("url", help="The long URL you want to shorten")
args = parser.parse_args()
try:
short = shorten(args.url)
except Exception as exc:
sys.exit(f"❌ Failed to shorten URL: {exc}")
print(f"🔗 Short link: {short}")
if CAN_COPY:
pyperclip.copy(short)
print("📋 Copied to clipboard!")
else:
print("⚠️ Install pyperclip (pip install pyperclip) to enable auto‑copy.")
if __name__ == "__main__":
main()
Why this works
(continued below 👇)
*
*
*
*
Next steps you can try on your own
* Add a custom alias – e.g.
* Support multiple URLs – accept a text file and output a CSV of long → short pairs.
* Pick a different API – TinyURL’s official endpoint gives you statistics, or Bitly if you want to play with authentication tokens.
Give it a spin, share the short link with a friend, and celebrate a tiny but real piece of automation you built from scratch. Happy coding! 🎉
requests.get(..., params={...}) builds the query string for you (?format=json&url=…) and handles HTTPS automatically. *
resp.raise_for_status() stops the script early if the server returns 4xx/5xx – a neat safety net for beginners. *
resp.json() parses the raw text into a Python dictionary, letting you access ["shorturl"] just like any other key‑value pair. *
argparse gives you a friendly -h/--help output for free, so anyone can see how to use the script without reading the code. Next steps you can try on your own
* Add a custom alias – e.g.
shorten instead of python shortener.py. * Support multiple URLs – accept a text file and output a CSV of long → short pairs.
* Pick a different API – TinyURL’s official endpoint gives you statistics, or Bitly if you want to play with authentication tokens.
Give it a spin, share the short link with a friend, and celebrate a tiny but real piece of automation you built from scratch. Happy coding! 🎉
❤1