#!/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
🐍 Python Tip for Absolute Beginners – Mastering List Slicing
Hey Python explorers! 👋
If you’ve ever felt a little lost when you need just a part of a list, today’s tip is for you. We’ll demystify list slicing – the super‑simple way to grab chunks of any list (or string) without a loop.
🔎 What is “slicing”?
Think of a list as a strip of paper with numbered stickers on it:
A slice is a cut‑out of that strip, defined by a start position, an end position, and an optional step (how many items to skip). Python does the cutting for you!
👉 How to slice a list
The basic syntax is:
- start – index where the slice begins (included). If omitted, Python starts at the beginning.
- stop – index where the slice ends (excluded). If omitted, Python goes to the end.
- step – how many items to jump each time. Default is
> Why does it work?
> Python stores lists as a continuous block of memory. Slicing simply creates a new view that points to the same underlying elements (for immutable types) or copies them (for mutable types). The
📚 Common slicing patterns (all bold for quick reference)
-
-
-
-
-
-
-
🛠️ Live example
Let’s play with a list of fruit names and see the slices in action:
What’s happening?
- In
- In
- In
- In
🚀 Why you’ll love slicing
- Speed – No need to write a loop; Python does the heavy lifting in C‑code, so it’s fast.
- Readability – One line tells the whole story, making your code easy for others (and future you) to understand.
- Flexibility – Works on any sequence: lists, strings, tuples, even custom objects that support slicing.
💡 Quick challenge:
Create a list of numbers 0‑20 (
1. All even numbers.
2. The middle three numbers.
3. The list in reverse order.
Give it a try, and drop your results in the comments! 🎉
Happy slicing, and see you in the next tip! 🌟
Hey Python explorers! 👋
If you’ve ever felt a little lost when you need just a part of a list, today’s tip is for you. We’ll demystify list slicing – the super‑simple way to grab chunks of any list (or string) without a loop.
🔎 What is “slicing”?
Think of a list as a strip of paper with numbered stickers on it:
index: 0 1 2 3 4 5 6
list : [10, 20, 30, 40, 50, 60, 70]
A slice is a cut‑out of that strip, defined by a start position, an end position, and an optional step (how many items to skip). Python does the cutting for you!
👉 How to slice a list
The basic syntax is:
new_list = old_list[start:stop:step]
- start – index where the slice begins (included). If omitted, Python starts at the beginning.
- stop – index where the slice ends (excluded). If omitted, Python goes to the end.
- step – how many items to jump each time. Default is
1.> Why does it work?
> Python stores lists as a continuous block of memory. Slicing simply creates a new view that points to the same underlying elements (for immutable types) or copies them (for mutable types). The
: operator tells the interpreter “take everything from start up to, but not including, stop”.📚 Common slicing patterns (all bold for quick reference)
-
my_list[:3] – first three items (indices 0, 1, 2) -
my_list[2:] – from the third item to the end -
my_list[1:5] – items at indices 1, 2, 3, 4 -
my_list[-3:] – last three items (negative indices count from the right) -
my_list[::-1] – the whole list reversed -
my_list[::2] – every second item (step = 2) -
my_list[5:1:-1] – items 5 → 2 in reverse order 🛠️ Live example
Let’s play with a list of fruit names and see the slices in action:
fruits = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape"]
# 1️⃣ First three fruits
first_three = fruits[:3]
print("First three:", first_three) # ['apple', 'banana', 'cherry']
# 2️⃣ From the fourth fruit to the end
from_fourth = fruits[3:]
print("From fourth:", from_fourth) # ['date', 'elderberry', 'fig', 'grape']
# 3️⃣ Every other fruit
every_other = fruits[::2]
print("Every other:", every_other) # ['apple', 'cherry', 'elderberry', 'grape']
# 4️⃣ Reverse the list
reversed_fruits = fruits[::-1]
print("Reversed:", reversed_fruits) # ['grape', 'fig', 'elderberry', 'date', 'cherry', 'banana', 'apple']
What’s happening?
- In
fruits[:3], Python starts at index 0 (default) and stops before index 3. - In
fruits[3:], it starts at index 3 and goes all the way to the end. - In
fruits[::2], the step 2 tells Python to skip one element each time. - In
fruits[::-1], a negative step means “move left”, effectively reversing the order.🚀 Why you’ll love slicing
- Speed – No need to write a loop; Python does the heavy lifting in C‑code, so it’s fast.
- Readability – One line tells the whole story, making your code easy for others (and future you) to understand.
- Flexibility – Works on any sequence: lists, strings, tuples, even custom objects that support slicing.
💡 Quick challenge:
Create a list of numbers 0‑20 (
range(21)) and use slicing to print:1. All even numbers.
2. The middle three numbers.
3. The list in reverse order.
Give it a try, and drop your results in the comments! 🎉
Happy slicing, and see you in the next tip! 🌟
👍1
🚀 One‑Sitting Python Project: “Instant Password Generator & Clipboard Copier”
Ever wish you could spin up a strong, random password in a second and have it land straight on your clipboard? This tiny utility does exactly that, and you can finish it while your coffee is still warm.
Why it’s a great starter project
- Immediate value – you’ll walk away with a tool you’ll actually use every day.
- Core Python concepts –
- A tiny third‑party library –
- All in one file – no complicated folder structure, just a single script you can run with
The roadmap (plain English)
1. Pick the pool of characters – letters (upper & lower), digits, and optional symbols (
2. Decide how long the password should be – let the user specify a length (default 12).
3. Randomly pull characters –
4. Show it on the screen –
5. Copy to clipboard (optional but cool) –
Why
Why
What you’ll need
- Python 3.x (any recent version).
- pyperclip – install once with
- No other dependencies – everything else lives in the standard library.
Quick peek at the code (copy‑paste into
How to run it
(continued below 👇)
Ever wish you could spin up a strong, random password in a second and have it land straight on your clipboard? This tiny utility does exactly that, and you can finish it while your coffee is still warm.
Why it’s a great starter project
- Immediate value – you’ll walk away with a tool you’ll actually use every day.
- Core Python concepts –
random, string, loops, functions, and handling command‑line arguments. - A tiny third‑party library –
pyperclip shows how to talk to the operating system without leaving Python. - All in one file – no complicated folder structure, just a single script you can run with
python password_gen.py.The roadmap (plain English)
1. Pick the pool of characters – letters (upper & lower), digits, and optional symbols (
!@#$…). 2. Decide how long the password should be – let the user specify a length (default 12).
3. Randomly pull characters –
random.choice picks one item from the pool each time; doing this length times builds a password. 4. Show it on the screen –
print lets you see the result instantly. 5. Copy to clipboard (optional but cool) –
pyperclip.copy hands the string over to the OS clipboard so you can paste it elsewhere without ever typing it.Why
random.choice works: it selects an element with equal probability from any sequence (list, string, etc.). By looping length times we get a truly random mix each run.Why
pyperclip works: under the hood it calls the appropriate system command (pbcopy on macOS, xclip/xsel on Linux, or the Windows clipboard API) so you don’t need to write platform‑specific code.What you’ll need
- Python 3.x (any recent version).
- pyperclip – install once with
pip install pyperclip. - No other dependencies – everything else lives in the standard library.
Quick peek at the code (copy‑paste into
password_gen.py and run!):#!/usr/bin/env python3
"""
Instant Password Generator & Clipboard Copier
Run: python password_gen.py [-l LENGTH] [-s]
-l LENGTH : Desired password length (default 12)
-s : Include symbols (!@#$%^&*)
"""
import argparse
import random
import string
import sys
try:
import pyperclip
CLIPBOARD_AVAILABLE = True
except ImportError:
CLIPBOARD_AVAILABLE = False
def build_charset(include_symbols: bool) -> str:
"""Create the pool of characters we can draw from."""
charset = string.ascii_letters + string.digits # A‑Z a‑z 0‑9
if include_symbols:
charset += "!@#$%^&*()-_=+[]{}|;:,.<>?/~`"
return charset
def generate_password(length: int, charset: str) -> str:
"""Pick `length` random characters from `charset`."""
# random.choice returns a single character each call
return ''.join(random.choice(charset) for _ in range(length))
def main():
parser = argparse.ArgumentParser(description="Create a strong random password.")
parser.add_argument("-l", "--length", type=int, default=12,
help="Length of the password (default: 12)")
parser.add_argument("-s", "--symbols", action="store_true",
help="Include punctuation symbols")
args = parser.parse_args()
if args.length <= 0:
print("❗ Length must be a positive integer.")
sys.exit(1)
charset = build_charset(args.symbols)
password = generate_password(args.length, charset)
print(f"🔐 Your new password: {password}")
if CLIPBOARD_AVAILABLE:
pyperclip.copy(password)
print("📋 Copied to clipboard!")
else:
print("⚠️ pyperclip not installed – run `pip install pyperclip` to enable clipboard copying.")
if __name__ == "__main__":
main()
How to run it
(continued below 👇)
# default 12‑character password, no symbols
python password_gen.py
# custom length, include symbols
python password_gen.py -l 20 -s
If you didn’t install
pyperclip yet, the script will still give you a password and politely remind you to install the library for clipboard support.🛠️ What you can tinker with next
- Add a strength meter – count how many character groups (upper, lower, digits, symbols) are present.
- Save to a file – write passwords to a local encrypted store (e.g., using
cryptography library). - GUI version – pop a tiny window with
tkinter (still in the standard library). Give it a go, share your first generated password in the comments, and watch how a handful of lines turn into a genuinely handy tool! 🎉
❤1
💡 Beginner‑Friendly Python Tip: Reading a Text File the Easy (and Safe) Way
When you start writing programs that need to work with real‑world data, the first step is often reading a file. Whether it’s a list of names, a CSV of scores, or a simple log, Python makes this surprisingly simple – and safe – if you use the right pattern.
Why “with open(...) as …” is your new best friend
-
- When you’re done, the file must be closed so the operating system can release the lock and free resources. Forgetting to close can lead to “file in use” errors or data loss.
- The
Step‑by‑step walk‑through
1️⃣ Open the file – tell Python where the file lives and how you want to read it.
2️⃣ Iterate over the file object – each iteration gives you one line as a string (including the newline
3️⃣ Strip the newline – most of the time you just want the text, not the invisible line break.
4️⃣ Do something with the line – print it, store it, count words, etc.
🔧 Full example (read a file called
What’s happening behind the scenes?
-
- The
-
-
⚡ Quick “what if” variations
- Read the whole file at once –
- Read into a list –
- Skip empty lines – add a simple guard:
🔑 Takeaway
- Always use
- Iterating directly over the file object is memory‑efficient; Python reads one line at a time, so even huge files won’t blow up your RAM.
-
Give it a try! Create a tiny
When you start writing programs that need to work with real‑world data, the first step is often reading a file. Whether it’s a list of names, a CSV of scores, or a simple log, Python makes this surprisingly simple – and safe – if you use the right pattern.
Why “with open(...) as …” is your new best friend
-
open() creates a file object – think of it as a remote control that lets you tell the computer “give me the next line”, “move forward”, etc. - When you’re done, the file must be closed so the operating system can release the lock and free resources. Forgetting to close can lead to “file in use” errors or data loss.
- The
with statement is a context manager. It automatically calls close() for you when the block finishes, even if an error occurs inside. No more “did I forget to close?” worries.Step‑by‑step walk‑through
1️⃣ Open the file – tell Python where the file lives and how you want to read it.
2️⃣ Iterate over the file object – each iteration gives you one line as a string (including the newline
\n). 3️⃣ Strip the newline – most of the time you just want the text, not the invisible line break.
4️⃣ Do something with the line – print it, store it, count words, etc.
🔧 Full example (read a file called
students.txt and print each name in uppercase):# students.txt might look like:
# Alice
# Bob
# Charlie
# Step 1: open the file safely with a context manager
with open('students.txt', mode='r', encoding='utf-8') as file:
# Step 2: loop over each line in the file
for line in file:
# Step 3: remove the trailing newline character
clean_line = line.strip()
# Step 4: work with the data – here we convert to uppercase
print(clean_line.upper())
What’s happening behind the scenes?
-
open('students.txt', mode='r', encoding='utf-8') tells Python to read ('r') the file using UTF‑8 text encoding (the most common text format). - The
with block creates a temporary scope. When the block ends, Python automatically runs file.close(). -
for line in file: leverages the fact that a file object is iterable – Python reads the next line each time the loop runs, without you having to call readline() manually. -
line.strip() removes any leading/trailing whitespace, including the newline \n that marks the end of each line in the file. ⚡ Quick “what if” variations
- Read the whole file at once –
content = file.read() returns one big string. Use this if the file is tiny and you need everything together. - Read into a list –
lines = file.readlines() gives a list where each element is a line (still with the newline). Combine with a list comprehension to clean them: with open('students.txt') as f:
names = [ln.strip() for ln in f]- Skip empty lines – add a simple guard:
if clean_line: # evaluates to False for '' (empty string)
print(clean_line)
🔑 Takeaway
- Always use
with open(...) as … – it guarantees the file is closed properly. - Iterating directly over the file object is memory‑efficient; Python reads one line at a time, so even huge files won’t blow up your RAM.
-
strip() is the quick way to tidy up each line before you use it.Give it a try! Create a tiny
students.txt file, run the script, and watch the names pop up in uppercase. Once you’re comfortable, you can start counting words, filtering data, or writing results back to a new file. Happy coding! 🚀Ever wanted a tiny personal assistant that lives right in your terminal? Imagine typing
Why this works
-
-
-
-
The big picture
1. Parse the command (
2. Load the existing JSON file (or start with an empty list if the file is missing).
3. Modify the list of tasks according to the command.
4. Write the updated list back to the same JSON file.
5. Print a friendly summary for the user.
Key concepts in plain language
- Dictionary: a “named box” (
- List: an ordered collection of dictionaries – each entry is a task.
- Serialization: turning Python objects into a text format (JSON) so they can be saved to disk.
- Deserialization: reading that text back into Python objects.
A minimal skeleton (copy‑paste & run)
(continued below 👇)
todo add "Buy milk" and seeing it saved instantly, then later running todo list to see everything waiting for you. You can build a single‑file command‑line To‑Do list in under an hour – no databases, no web servers, just pure Python.Why this works
-
argparse turns ordinary words you type into actions (add, list, done, delete). It reads sys.argv for you, so you don’t have to manually slice strings.-
json gives you a human‑readable file format. Python can turn a list of dictionaries into a text file with json.dump and back again with json.load. No extra libraries needed.-
pathlib lets you handle file paths in a cross‑platform way (Path.home() / ".my_todo.json"). It’s safer than concatenating strings with "/" or "\\“.-
datetime stamps each task so you can see when you created it – useful for “what’s overdue?”.The big picture
1. Parse the command (
add, list, done, remove) with argparse.2. Load the existing JSON file (or start with an empty list if the file is missing).
3. Modify the list of tasks according to the command.
4. Write the updated list back to the same JSON file.
5. Print a friendly summary for the user.
Key concepts in plain language
- Dictionary: a “named box” (
{'text': 'Buy milk', 'done': False}) that stores data with labels.- List: an ordered collection of dictionaries – each entry is a task.
- Serialization: turning Python objects into a text format (JSON) so they can be saved to disk.
- Deserialization: reading that text back into Python objects.
A minimal skeleton (copy‑paste & run)
(continued below 👇)
#!/usr/bin/env python3
import argparse
import json
from pathlib import Path
from datetime import datetime
# Where we keep the data
DATA_FILE = Path.home() / ".my_todo.json"
def load_tasks():
if DATA_FILE.exists():
return json.load(DATA_FILE.open())
return [] # start empty
def save_tasks(tasks):
DATA_FILE.write_text(json.dumps(tasks, indent=2))
def add_task(text):
tasks = load_tasks()
tasks.append({
"text": text,
"done": False,
"created": datetime.now().isoformat()
})
save_tasks(tasks)
print(f'✅ Added: "{text}"')
def list_tasks():
tasks = load_tasks()
if not tasks:
print("🗒️ No tasks yet!")
return
for i, t in enumerate(tasks, 1):
status = "✔️" if t["done"] else "❌"
print(f"{i}. {status} {t['text']} (added {t['created'][:10]})")
def mark_done(idx):
tasks = load_tasks()
try:
tasks[idx]["done"] = True
save_tasks(tasks)
print(f'✅ Marked task #{idx+1} as done')
except IndexError:
print("⚠️ No such task number")
def remove_task(idx):
tasks = load_tasks()
try:
removed = tasks.pop(idx)
save_tasks(tasks)
print(f'🗑️ Removed: "{removed["text"]}"')
except IndexError:
print("⚠️ No such task number")
def main():
parser = argparse.ArgumentParser(prog="todo", description="Simple terminal to‑do list")
sub = parser.add_subparsers(dest="cmd")
add = sub.add_parser("add")
add.add_argument("text", help="Task description")
sub.add_parser("list")
done = sub.add_parser("done")
done.add_argument("num", type=int, help="Task number to mark done")
rm = sub.add_parser("remove")
rm.add_argument("num", type=int, help="Task number to delete")
args = parser.parse_args()
if args.cmd == "add":
add_task(args.text)
elif args.cmd == "list":
list_tasks()
elif args.cmd == "done":
mark_done(args.num - 1) # human numbers start at 1
elif args.cmd == "remove":
remove_task(args.num - 1)
else:
parser.print_help()
if __name__ == "__main__":
main()
How to run it
1. Save the script as
todo.py and make it executable (chmod +x todo.py on Linux/macOS).2. Run
./todo.py add "Read a chapter of a book" → task saved.3.
./todo.py list → see all tasks with ✅/❌.4.
./todo.py done 1 → mark the first task as done.5.
./todo.py remove 2 → delete the second task.That’s it – a fully functional, persistent to‑do list you built yourself! Tweak it by adding priorities, due dates, or even a tiny web UI later. The core ideas (argument parsing, JSON storage, pathlib paths) are building blocks you’ll reuse in countless Python projects.
❤1
Ever needed to walk through two (or more) lists at the same time?
Writing a loop with an index variable works, but there’s a cleaner, safer way:
What
Each tuple contains the items that share the same position in the original iterables.
Why it works
1. Parallel stepping – Internally
2. Stops at the shortest – If the iterables have different lengths,
3. No explicit counter – You never have to manage
When to prefer
- Readability –
- Safety – No chance of forgetting to increment the index or using the wrong list length.
- Flexibility – Works with any iterable, not just lists (e.g., strings,
Quick comparison
Manual index
With
Key take‑aways
- Use
- It automatically stops at the shortest sequence – perfect for avoiding accidental over‑runs.
- You can unpack the tuple directly in the
Give it a try! Replace any “loop‑by‑index” pattern with
Writing a loop with an index variable works, but there’s a cleaner, safer way:
zip().What
zip() does zip() takes any number of iterables (lists, strings, ranges…) and creates a new iterator that yields tuples. Each tuple contains the items that share the same position in the original iterables.
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name} scored {score}")
Why it works
1. Parallel stepping – Internally
zip asks each iterable for its next element, then bundles them together. 2. Stops at the shortest – If the iterables have different lengths,
zip stops when the first one runs out, preventing an “index out of range” crash. 3. No explicit counter – You never have to manage
i = 0; i += 1, so the code is less error‑prone and easier to read.When to prefer
zip over manual indexing- Readability –
for name, score in zip(...): reads like plain English. - Safety – No chance of forgetting to increment the index or using the wrong list length.
- Flexibility – Works with any iterable, not just lists (e.g., strings,
range(), generators).Quick comparison
Manual index
for i in range(len(names)):
print(names[i], scores[i])
With
zip for name, score in zip(names, scores):
print(name, score)
Key take‑aways
- Use
zip() whenever you need to pair items from multiple sequences. - It automatically stops at the shortest sequence – perfect for avoiding accidental over‑runs.
- You can unpack the tuple directly in the
for header, giving you meaningful variable names right away.Give it a try! Replace any “loop‑by‑index” pattern with
zip and watch your code become cleaner and more Pythonic.❤1
Ever stared at a blank terminal and thought “I could use a spark of inspiration right now”? You can turn that feeling into a tiny, useful Python project that both brightens your day and builds a personal “quote‑archive” you can flip through later. In under an hour you’ll have a command‑line daily‑quote fetcher that pulls a random quote from the web, shows it instantly, and logs it with a timestamp in a plain‑text file.
What you’ll need
-
-
-
-
- File I/O – using
Why these pieces fit together
1. Fetching data –
2. Turning text into data –
3. Choosing one –
4. Recording the moment –
5. Saving without wiping – opening a file with mode
Sketch of the script
How it works, line by line
(continued below 👇)
What you’ll need
-
requests – a friendly library that hides the low‑level details of talking to websites (it does the HTTP GET for you). -
json – Python’s built‑in way to turn the text you get back from the web into real Python objects (lists, dictionaries). -
datetime – lets you stamp each quote with the exact day and time it was fetched. -
random – picks a random element from a list (so you don’t always see the same quote). - File I/O – using
open(..., "a") to append new lines to a log file without overwriting anything.Why these pieces fit together
1. Fetching data –
requests.get(url) sends a GET request to the API endpoint. The server replies with a payload (usually JSON). 2. Turning text into data –
response.json() parses that JSON string into ordinary Python structures (a list of dictionaries, each representing a quote). 3. Choosing one –
random.choice(quotes) picks a random dictionary from the list, giving you a fresh quote each run. 4. Recording the moment –
datetime.now().strftime("%Y-%m-%d %H:%M") formats the current date‑time as a readable string, so your log reads “2026‑09‑29 14:03 – …”. 5. Saving without wiping – opening a file with mode
"a" means “add to the end”. Every new quote gets a new line, preserving the whole history.Sketch of the script
import requests # talk to the web
import random # pick a random item
from datetime import datetime # timestamp
import json # (optional) for pretty‑printing
# 1️⃣ The free quote API – it returns a big list of quote objects
API_URL = "https://type.fit/api/quotes"
def fetch_random_quote():
resp = requests.get(API_URL, timeout=10) # ask the server
resp.raise_for_status() # raise an error if something went wrong
quotes = resp.json() # turn JSON text into Python list
return random.choice(quotes) # pick one at random
def log_quote(quote, file_path="quotes_log.txt"):
ts = datetime.now().strftime("%Y-%m-%d %H:%M")
line = f"{ts} – {quote['text']} — {quote.get('author') or 'Unknown'}\n"
with open(file_path, "a", encoding="utf-8") as f: # open for appending
f.write(line)
def main():
q = fetch_random_quote()
# 2️⃣ Show it nicely in the terminal
author = q.get('author') or "Unknown"
print("\n💡 Quote of the moment:\n")
print(f"\"{q['text']}\"")
print(f" — {author}\n")
# 3️⃣ Save it for later browsing
log_quote(q)
if __name__ == "__main__":
main()
How it works, line by line
(continued below 👇)