Py Snack 🍿
50 subscribers
2 links
Bite-sized Python tips to write cleaner, smarter, and faster code. Learn best practices, refactoring techniques, and optimization strategies to improve your Python skills and code quality.
📬 Feedback, questions, or collaboration? Reach out: @bashnxa
Download Telegram
💡 In Python 3.14, uuid6, uuid7, and uuid8 were added:

UUID v6 → monotonic, time-ordered replacement for v1 (better sorting, no MAC leaks).

UUID v7 → combines timestamp ordering with randomness → great for DB primary keys.

UUID v8 → custom format playground for app-specific needs.

📌 #python_3_14 #uuid #databases #performance #customization
🔥1
🚫 Before:
class UserReport:
def __init__(self, user_id):
self.user_id = user_id
def total_spent(self):
print("Querying database...")
time.sleep(2) # expensive call
return 199.99
r = UserReport(123)
print(r.total_spent()) # recomputes every call
print(r.total_spent())

✅ After:
from functools import cached_property
import time
class UserReport:
def __init__(self, user_id):
self.user_id = user_id
@cached_property
def total_spent(self):
print("Querying database...")
time.sleep(2) # expensive call
return 199.99
r = UserReport(123)
print(r.total_spent) # computed once
print(r.total_spent) # cached instantly
del r.__dict__["total_spent"]
print(r.total_spent) # recomputed after cache reset

🧠 ➤ @cached_property is ideal for expensive computations per object (e.g., DB queries, API calls, configurations).
➤ The result is stored in the instance's dict.
➤ Delete it to force a recomputation.
📌 #basics #functools #cache #lazy #performance #clean_code #oop
🚫 Before:
user_input = "۹"
if user_input.isdigit():
number = int(user_input) # 9

✅ After:
import re

user_input = "۹"
if re.fullmatch(r"[0-9]+", user_input):
number = int(user_input)

🧠 ➤ Python treats hundreds of Unicode characters as digits (Nd category), not just 0–9.
Use a strict ASCII regex ([0-9]) when you need to accept only standard digits.
📌 #basics #unicode #pitfalls #regex #clean_code #python_internals
🚫 Before:
def process_data(items):
l = len # Local alias for global function
for item in items:
if l(item) > 10: # Using local alias
process(item)

✅ After(3.11+):
def process_data(items):
for item in items:
if len(item) > 10: # Direct call is fine now!
process(item)

🧠 ➤ In Python 3.11+: LOAD_GLOBAL specializes to LOAD_GLOBAL_BUILTIN with cached access — nearly as fast as local lookups!
📌 #performance #optimization #python_3_11 #clean_code #bytecode
🚫 Before:
def process_data(data):
result = []
for item in data:
if validate_item(item):
item = item.strip().lower()
result.append(item)
return result

✅ After:
def process_data(data):
return [clean_item(item) for item in data if validate_item(item)]

🧠 ➤ Trust other functions instead of duplicating logic.
📌 #softskills #clean_code #trust #delegation #pythonic #teamwork
🚫 Before:
s = "user: {name} — role: {role}".format(name=user, role=role)

✅ After(3.14+):
from string.templatelib import Template

def render(template: Template):
parts = []
for item in template:
if isinstance(item, str):
parts.append(item)
return "".join(parts)

user = "Alice"
role = "admin"
t = t"user: {user} — role: {role}"
s = render(t)

🧠 ➤ Python 3.14 adds t-strings (PEP 750) — template literals similar to f-strings but with deferred substitution.
📌 #basics #python_3_14 #templates #strings #clean_code #tips
👍1
🚫 Before:
class Worker:
def __init__(self):
self.buffer = []
def process(self, data):
self.buffer.append(data)
return transform(data)
def close(self):
self.buffer.clear()

✅ After:
from contextlib import ExitStack

class Worker:
def __init__(self):
self.buffer = []
self._stack = ExitStack()
self._stack.callback(self.buffer.clear)
def process(self, data):
self.buffer.append(data)
return transform(data)
def close(self):
self._stack.close()

🧠 ➤ ExitStack guarantees cleanup even if exceptions occur
📌 #contextlib #memory #clean_code #resource_safety
🚫 Before:
@app.post("/order")
async def create_order(order: OrderCreate, session: Session = Depends(get_db)):
db_order = Order(**order.model_dump())
session.add(db_order)
session.flush()
payment_id = await payment_service.charge(db_order.id)
db_order.payment_id = payment_id
session.commit()
return db_order

✅ After:
@app.post("/order")
async def create_order(order: OrderCreate, session: Session = Depends(get_db)):
db_order = Order(**order.model_dump())
session.add(db_order)
await session.commit()
payment_id = await payment_service.charge(db_order.id)
session.add(session.merge(Order(id=db_order.id, payment_id=payment_id)))
await session.commit()
return db_order

🧠 ➤ Never perform async I/O (e.g., HTTP calls) inside a database transaction — it holds locks and risks deadlocks or timeouts. Commit early, act externally, then update in a new transaction.
📌 #fastapi #sqlalchemy #transactions #pitfalls #asyncio
🚫 Before:
def first(items):
return items[0] if len(items) > 0 else None

✅ After:
def first(items):
return next(iter(items), None)

🧠 ➤ Works for any iterable — not just lists.
📌 #iterators #pythonic #tips
🚫 Before:
try:
os.remove(path)
except FileNotFoundError:
pass

✅ After:
from contextlib import suppress

with suppress(FileNotFoundError):
os.remove(path)

🧠 ➤ contextlib.suppress() expresses intentional error ignoring explicitly, avoids empty except blocks, and scales cleanly when suppressing multiple exception types.
📌 #clean_code #contextlib #exceptions #pythonic #best_practices
🚫 Before:
from dataclasses import dataclass
@dataclass
class User:
name: str
tags: list[str] = None

✅ After:
from dataclasses import dataclass, field
@dataclass
class User:
name: str
tags: list[str] = field(default_factory=list)

🧠 ➤ default_factory creates a new list per instance instead of sharing one.
➤ tags = None breaks typing and forces extra None checks everywhere.
➤ This is the same mutable default pitfall — just inside dataclasses.
📌 #dataclasses #pitfalls #typing #clean_code #best_practices