Skip to content

Commit af7524f

Browse files
committed
Sample code for the article on best practices
1 parent 0e25d33 commit af7524f

22 files changed

+585
-0
lines changed

python-best-practices/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Python Best Practices: From Messy to Pythonic Code
2+
3+
This folder provides the code examples for the Real Python tutorial [Python Best Practices: From Messy to Pythonic Code](https://realpython.com/python-best-practice/).

python-best-practices/classes.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Avoid this:
2+
# class Article:
3+
# def __init__(self, title, body, tags, db):
4+
# self.title = title
5+
# self.body = body
6+
# self.tags = tags or []
7+
# self.db = db
8+
# self.slug = None
9+
# self.published = False
10+
11+
# def publish(self):
12+
# if self.slug is None:
13+
# self.slug = "-".join(self.title.lower().split())
14+
15+
# self.db.save_article(
16+
# title=self.title,
17+
# body=self.body,
18+
# tags=self.tags,
19+
# slug=self.slug,
20+
# )
21+
22+
# self.published = True
23+
24+
25+
# Favor this:
26+
from dataclasses import dataclass, field
27+
from datetime import datetime
28+
29+
30+
@dataclass
31+
class Article:
32+
title: str
33+
body: str
34+
tags: list[str] = field(default_factory=list)
35+
created_at: datetime = field(default_factory=datetime.utcnow)
36+
published_at: datetime | None = None
37+
38+
@property
39+
def is_published(self) -> bool:
40+
return self.published_at is not None
41+
42+
@property
43+
def slug(self) -> str:
44+
return "-".join(self.title.lower().split())
45+
46+
def __str__(self) -> str:
47+
status = "published" if self.is_published else "draft"
48+
return f"{self.title} [{status}]"
49+
50+
51+
class Publisher:
52+
def __init__(self, db):
53+
self._db = db
54+
55+
def publish(self, article: Article) -> None:
56+
if article.is_published:
57+
return
58+
article.published_at = datetime.utcnow()
59+
self._db.save(article)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Avoid this:
2+
# def addNum(a,b):return a+ b
3+
4+
5+
# Favor this:
6+
def add_numbers(a, b):
7+
return a + b

python-best-practices/comments.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Avoid this:
2+
# def find_index(sorted_items, target):
3+
# # Set left index
4+
# left = 0
5+
# # Set right index
6+
# right = len(sorted_items) - 1
7+
# # Loop while left is less than right
8+
# while left <= right:
9+
# # Compute middle
10+
# mid = (left + right) // 2
11+
# # Check if equal
12+
# if sorted_items[mid] == target:
13+
# # Return mid
14+
# return mid
15+
# # Check if less than target
16+
# elif sorted_items[mid] < target:
17+
# # Move left up
18+
# left = mid + 1
19+
# else:
20+
# # Move right down
21+
# right = mid - 1
22+
# # Return -1 if not found
23+
# return -1
24+
25+
26+
# Favor this:
27+
def find_index(sorted_items, target):
28+
"""Return the index of target in sorted_items, or -1 if not found."""
29+
left = 0
30+
right = len(sorted_items) - 1
31+
32+
while left <= right:
33+
mid = (left + right) // 2
34+
value = sorted_items[mid]
35+
36+
if value == target:
37+
return mid
38+
39+
# If target is larger, you can safely ignore the left half
40+
if value < target:
41+
left = mid + 1
42+
# Otherwise, target must be in the left half (if present)
43+
else:
44+
right = mid - 1
45+
46+
return -1
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Avoid this:
2+
# cubes = []
3+
# for number in range(10):
4+
# cubes.append(number**3)
5+
6+
# Favor this:
7+
cubes = [number**3 for number in range(10)]
8+
cubes
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Avoid this:
2+
# import asyncio
3+
4+
# import requests
5+
6+
# async def main():
7+
# await asyncio.gather(
8+
# fetch_status("https://example.com"),
9+
# fetch_status("https://python.org"),
10+
# )
11+
12+
# async def fetch_status(url):
13+
# response = requests.get(url) # Blocking I/O task
14+
# return response.status_code
15+
16+
# asyncio.run(main())
17+
18+
19+
# Favor this:
20+
import asyncio
21+
22+
import aiohttp
23+
24+
25+
async def main():
26+
async with aiohttp.ClientSession() as session:
27+
statuses = await asyncio.gather(
28+
fetch_status(session, "https://example.com"),
29+
fetch_status(session, "https://realpython.com"),
30+
)
31+
print(statuses)
32+
33+
34+
async def fetch_status(session, url):
35+
async with session.get(url) as response: # Non-blocking I/O task
36+
return response.status
37+
38+
39+
asyncio.run(main())
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Avoid this:
2+
# def shipping_cost(country, items):
3+
# if country is not None:
4+
# if country == "US":
5+
# if len(items) > 0: # Non-empty cart?
6+
# if len(items) > 10: # Free shipping?
7+
# return 0
8+
# else:
9+
# return 5
10+
# else:
11+
# return 0
12+
# elif country == "CA":
13+
# if len(items) > 0: # Non-empty cart?
14+
# return 10
15+
# else:
16+
# return 0
17+
# else:
18+
# # Other countries
19+
# if len(items) > 0: # Non-empty cart?
20+
# return 20
21+
# else:
22+
# return 0
23+
# else:
24+
# raise ValueError("invalid country")
25+
26+
27+
# Favor this:
28+
def shipping_cost(country, items):
29+
if country is None:
30+
raise ValueError("invalid country")
31+
32+
if not items: # Empty cart?
33+
return 0
34+
35+
if country == "US":
36+
return 0 if len(items) > 10 else 5
37+
38+
if country == "CA":
39+
return 10
40+
41+
return 20 # Other countries
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Avoid this:
2+
# def add(a, b):
3+
# """Return the sum of a and b."""
4+
# return a + b
5+
6+
7+
# Favor this:
8+
def add(a, b):
9+
"""Sum two numbers.
10+
11+
Args:
12+
a (int or float): The first number.
13+
b (int or float): The second number.
14+
15+
Returns:
16+
int or float: The sum of the two numbers.
17+
"""
18+
return a + b
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Avoid this:
2+
# import json
3+
4+
5+
# def load_config(path):
6+
# try:
7+
# with open(path, encoding="utf-8") as config:
8+
# data = json.load(config)
9+
# except Exception:
10+
# # If something went wrong, return an empty config
11+
# return {}
12+
# return data
13+
14+
15+
# def main():
16+
# try:
17+
# config = load_config("settings.json")
18+
# except Exception:
19+
# print("Sorry, something went wrong.")
20+
# # Do something with config...
21+
22+
23+
# Favor this:
24+
import json
25+
import logging
26+
27+
log = logging.getLogger(__name__)
28+
29+
30+
class ConfigError(Exception):
31+
"""Raised when issues occur with config file."""
32+
33+
34+
def load_config(path):
35+
try:
36+
with open(path, encoding="utf-8") as config:
37+
data = json.load(config)
38+
except FileNotFoundError as error:
39+
raise ConfigError(f"Config file not found: {path}") from error
40+
except json.JSONDecodeError as error:
41+
raise ConfigError(f"Invalid JSON: {path}") from error
42+
return data
43+
44+
45+
def main():
46+
try:
47+
config = load_config("settings.json")
48+
print("Config:", config)
49+
except ConfigError as error:
50+
log.error("Error loading the config: %s", error)
51+
print("Sorry, something went wrong while loading the settings.")
52+
# Do something with config...

python-best-practices/functions.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Avoid this:
2+
# import csv
3+
4+
# def process_users(users, min_age, filename, send_email):
5+
# adults = []
6+
# for user in users:
7+
# if user["age"] >= min_age:
8+
# adults.append(user)
9+
10+
# with open(filename, mode="w", newline="", encoding="utf-8") as csv_file:
11+
# writer = csv.writer(csv_file)
12+
# writer.writerow(["name", "age"])
13+
# for user in adults:
14+
# writer.writerow([user["name"], user["age"]])
15+
16+
# if send_email:
17+
# # Emailing logic here...
18+
19+
# return adults, filename
20+
21+
# Favor this:
22+
import csv
23+
24+
25+
def filter_adult_users(users, *, min_age=18):
26+
"""Return users whose age is at least min_age."""
27+
return [user for user in users if user["age"] >= min_age]
28+
29+
30+
def save_users_csv(users, filename):
31+
"""Save users to a CSV file."""
32+
with open(filename, mode="w", newline="", encoding="utf-8") as csv_file:
33+
writer = csv.writer(csv_file)
34+
writer.writerow(["name", "age"])
35+
for user in users:
36+
writer.writerow([user["name"], user["age"]])
37+
38+
39+
def send_users_report(filename):
40+
"""Send the report."""
41+
# Emailing logic here...

0 commit comments

Comments
 (0)