🚀 Boost your file handling with
Tired of fiddling with string concatenations for paths?
Why you’ll love it:
- No more
- Works the same on Windows, macOS, Linux.
- Handy methods like
Give it a try and say goodbye to brittle path strings! 🎉
pathlib! Tired of fiddling with string concatenations for paths?
pathlib lets you treat file paths like objects – safe, readable, and cross‑platform.from pathlib import Path
# 📁 Build a path to "data/report.txt" inside the user's home folder
home = Path.home()
report_path = home / "data" / "report.txt"
# ✅ Check if it exists, read its content, and print the first line
if report_path.is_file():
first_line = report_path.read_text().splitlines()[0]
print(f"🗒️ First line: {first_line}")
else:
print("❌ File not found!")
Why you’ll love it:
- No more
os.path.join headaches. - Works the same on Windows, macOS, Linux.
- Handy methods like
.is_file(), .read_text(), .write_text() keep code clean.Give it a try and say goodbye to brittle path strings! 🎉
💡 Quick tip: the Walrus operator (
What’s happening?
-
- The same expression is immediately checked with
No extra
:=) lets you assign and test a value in a single line. Great for reading input, looping over data, or any situation where you’d otherwise need two statements.# Read numbers until a non‑numeric line appears
while (line := input("Enter a number (or anything else to stop): ")) .isdigit():
number = int(line)
print(f"✅ You entered {number}")
print("🚪 Done!")
What’s happening?
-
line := input(...) captures the user’s input. - The same expression is immediately checked with
.isdigit(). No extra
line = input(...) line, no duplicate calls—cleaner and faster to write. Give it a try in your next script! 🚀💡 Silently ignore the errors you expect – without a try/except clutter
When you have a piece of code that may raise a known exception (e.g., trying to delete a file that might already be gone), wrapping it in a full
✅ Why you’ll love it
- No extra indentation levels – your main logic stays front‑and‑center.
- Explicit about which exception is being ignored, so it’s safe and self‑documenting.
- Works with any exception type, even custom ones.
Give it a try next time you need a “try‑and‑forget” cleanup! 🚀
When you have a piece of code that may raise a known exception (e.g., trying to delete a file that might already be gone), wrapping it in a full
try/except block can feel noisy. contextlib.suppress does the same thing in a single line and keeps the flow readable.from contextlib import suppress
import os
# Remove a temp file if it exists – ignore the FileNotFoundError otherwise
with suppress(FileNotFoundError):
os.remove('temp_data.json')
✅ Why you’ll love it
- No extra indentation levels – your main logic stays front‑and‑center.
- Explicit about which exception is being ignored, so it’s safe and self‑documenting.
- Works with any exception type, even custom ones.
Give it a try next time you need a “try‑and‑forget” cleanup! 🚀
❤1
🚀 One‑liner to count & rank items in a list
Ever needed a quick frequency report (e.g., most used words, top error codes) without writing a loop? 🎯
Why it’s cool
- Fast – implemented in C, O(n) time.
- Readable – the intent is obvious at a glance.
- Flexible –
Next time you need a quick tally, reach for
Ever needed a quick frequency report (e.g., most used words, top error codes) without writing a loop? 🎯
collections.Counter does it in a single line and gives you handy helpers like most_common.from collections import Counter
data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
top = Counter(data).most_common(2) # → [('apple', 3), ('banana', 2)]
print(top)
Why it’s cool
- Fast – implemented in C, O(n) time.
- Readable – the intent is obvious at a glance.
- Flexible –
most_common() can return all items or just the top N.Next time you need a quick tally, reach for
Counter and skip the manual dictionary gymnastics! 🌟💡 Merge dictionaries in one clean line – the
When you need a base config and want to overlay user‑provided values, the classic
Want to update in place? Use the in‑place version:
No loops, no
| operator (Python 3.9+)When you need a base config and want to overlay user‑provided values, the classic
dict.update() mutates the original. With the new pipe syntax you can keep both dictionaries untouched and get a brand‑new combined mapping.# default settings
default_cfg = {
"host": "localhost",
"port": 8000,
"debug": False,
}
# values supplied by the user (maybe from a CLI or a JSON file)
user_cfg = {
"port": 8080,
"debug": True,
}
# 👇 merge → user_cfg wins on overlapping keys
merged_cfg = default_cfg | user_cfg
print(merged_cfg)
# {'host': 'localhost', 'port': 8080, 'debug': True}
Want to update in place? Use the in‑place version:
default_cfg |= user_cfg # modifies default_cfg
No loops, no
copy() calls – just a single, readable operator. Perfect for settings, API payloads, or any situation where you blend two maps. 🚀💡 Keep the order, drop the duplicates – in one line
Got a list with repeating items and need the first occurrence of each element, preserving the original order?
Python’s
* How it works
*
* Converting the dict back to a list (
🔧 When to use: cleaning up CSV columns, de‑duplicating user input, or any place you need “unique but ordered” without pulling in
Give it a try – it’s both fast and readable! 🚀
Got a list with repeating items and need the first occurrence of each element, preserving the original order?
Python’s
dict (since 3.7) remembers insertion order, so you can turn the sequence into keys and back again:>>> data = ["apple", "banana", "apple", "orange", "banana", "grape"]
>>> unique = list(dict.fromkeys(data))
>>> unique
['apple', 'banana', 'orange', 'grape']
* How it works
*
dict.fromkeys(data) creates a dict where each list item becomes a key – duplicates are automatically discarded. * Converting the dict back to a list (
list(...)) gives you the values in the exact order they first appeared.🔧 When to use: cleaning up CSV columns, de‑duplicating user input, or any place you need “unique but ordered” without pulling in
OrderedDict or extra loops. Give it a try – it’s both fast and readable! 🚀
🚀 Dataclasses + mutable defaults – the hidden trap and the clean fix
If you’ve ever written a class like this:
you probably noticed a weird behavior: every new
Why does this happen?
In Python, default argument values are evaluated only once – at function definition time. The empty list
The dataclass‑friendly solution:
How it works under the hood
- When the dataclass decorator builds
- At call time,
- The generated
Because the factory is a callable, you can pass any function that returns the desired mutable type, even a lambda that captures external state.
Real‑world use case: building a tree of nodes
Imagine you’re modeling a simple filesystem:
If you had used
Edge case: custom factories with arguments
Sometimes you need a default that isn’t just an empty container, e.g. a list pre‑filled with a timestamp:
Because the factory is called every time a new
Quick checklist ✅
- Never use a mutable object (
- Use
- For functions, replace the mutable default with
- Remember: the factory can be any zero‑argument callable, giving you full flexibility.
💡 Takeaway: The
If you’ve ever written a class like this:
class Bag:
def __init__(self, items=[]):
self.items = items
you probably noticed a weird behavior: every new
Bag() shares the same list. Adding an item to one bag magically appears in all the others. 😱 Why does this happen?
In Python, default argument values are evaluated only once – at function definition time. The empty list
[] is created a single time and then stored in the function’s __defaults__. Every call that doesn’t supply its own items argument re‑uses that very same list object. The same rule applies to dataclasses because their generated __init__ works just like a normal function.The dataclass‑friendly solution:
field(default_factory=…)dataclasses.field lets you specify a factory – a callable that is invoked each time a new instance is created. The factory returns a fresh object, so every instance gets its own independent mutable container.from dataclasses import dataclass, field
@dataclass
class Bag:
# ✅ each Bag gets its own list
items: list = field(default_factory=list)
# you can also use other factories, e.g. set() or dict()
How it works under the hood
- When the dataclass decorator builds
Bag.__init__, it inserts items: list = field(default_factory=list).- At call time,
default_factory() is executed, producing a brand‑new list.- The generated
__init__ assigns that fresh list to self.items.Because the factory is a callable, you can pass any function that returns the desired mutable type, even a lambda that captures external state.
Real‑world use case: building a tree of nodes
Imagine you’re modeling a simple filesystem:
from __future__ import annotations
from dataclasses import dataclass, field
@dataclass
class Node:
name: str
children: list[Node] = field(default_factory=list)
def add_child(self, child: Node) -> None:
self.children.append(child)
# Build a tiny hierarchy
root = Node("root")
root.add_child(Node("etc"))
root.add_child(Node("var"))
print(root)
If you had used
children=[] directly, every Node would share the same children list, and adding a child to one node would magically appear under all others – a disaster for tree structures.Edge case: custom factories with arguments
Sometimes you need a default that isn’t just an empty container, e.g. a list pre‑filled with a timestamp:
import datetime
from dataclasses import dataclass, field
def timestamped_list():
# each instance gets its own list containing the creation time
return [datetime.datetime.utcnow()]
@dataclass
class EventLog:
entries: list[datetime.datetime] = field(default_factory=timestamped_list)
log1 = EventLog()
log2 = EventLog()
print(log1.entries, log2.entries) # different timestamps
Because the factory is called every time a new
EventLog is instantiated, each list carries its own unique data.Quick checklist ✅
- Never use a mutable object (
[], {}, set()) directly as a default value in a dataclass or function.- Use
field(default_factory=…) for dataclasses.- For functions, replace the mutable default with
None and create the object inside the body (if arg is None: arg = []).- Remember: the factory can be any zero‑argument callable, giving you full flexibility.
💡 Takeaway: The
default_factory pattern gives you the same convenience as a simple default argument, but without the shared‑state surprise. Your dataclasses will behave predictably, and you’ll avoid a whole class of subtle bugs that even seasoned developers stumble over. Happy coding! 🚀Hey Python newbies! 🌟
Let’s talk about something that makes Python look clean and tidy: indentation.
In many languages you need special symbols (
Why does indentation matter?
1. It defines “blocks”.
A block is a group of lines that should be executed together.
- When you write an
- The same goes for loops (
2. It prevents mistakes.
Because the structure is visible, you can spot missing or extra spaces quickly.
If the indentation is wrong, Python will raise an IndentationError and tell you exactly where the problem is.
3. It keeps code readable.
When you (or anyone else) open the file later, the visual layout instantly shows the logic flow.
How does it work?
- Choose either spaces or a tab for the whole file – don’t mix them.
- The most common practice (and what the official style guide, PEP 8, recommends) is 4 spaces per level.
- Every time you start a new block, add one more level of indentation; when the block ends, go back to the previous level.
Let’s see a real example – a tiny program that asks for a number and tells you if it’s even or odd:
What’s happening step‑by‑step?
1.
2.
3.
- If the remainder is
- If not, it jumps to the
Notice how the two
Quick checklist for clean indentation
- Consistent width: 4 spaces each level (no tabs unless you set your editor to insert 4 spaces for a tab).
- Never mix spaces and tabs in the same file.
- Indent after a colon (
- Dedent (remove one level) when the block ends.
Try it yourself!
1. Open a new file called
2. Copy the code above, paying special attention to the spaces before the
3. Run it with
If you get an IndentationError, look at the line number Python mentions – the spaces are probably off.
Bottom line:
Indentation isn’t a cosmetic detail; it’s the language’s way of understanding your logic. Mastering it early will make every Python concept feel clearer and your code look beautiful. Keep an eye on those four spaces, and you’ll never get lost in a sea of braces again! 🚀
Let’s talk about something that makes Python look clean and tidy: indentation.
In many languages you need special symbols (
{ } or end) to show where a block of code starts and stops. Python chose a different path – it uses spaces (or tabs) at the beginning of a line. This isn’t just for looks; it tells the computer which lines belong together.Why does indentation matter?
1. It defines “blocks”.
A block is a group of lines that should be executed together.
- When you write an
if statement, everything indented under it runs only if the condition is true. - The same goes for loops (
for, while) and functions.2. It prevents mistakes.
Because the structure is visible, you can spot missing or extra spaces quickly.
If the indentation is wrong, Python will raise an IndentationError and tell you exactly where the problem is.
3. It keeps code readable.
When you (or anyone else) open the file later, the visual layout instantly shows the logic flow.
How does it work?
- Choose either spaces or a tab for the whole file – don’t mix them.
- The most common practice (and what the official style guide, PEP 8, recommends) is 4 spaces per level.
- Every time you start a new block, add one more level of indentation; when the block ends, go back to the previous level.
Let’s see a real example – a tiny program that asks for a number and tells you if it’s even or odd:
# Ask the user for a number
number_str = input("Enter a whole number: ")
# Convert the text we got into an actual integer (a number)
number = int(number_str)
# Check if the number is even or odd
if number % 2 == 0: # % is the remainder operator
print(f"{number} is **even**.") # This line is inside the if‑block
else:
print(f"{number} is **odd**.") # This line is inside the else‑block
What’s happening step‑by‑step?
1.
input() shows a prompt and waits for you to type something. It always returns text (str). 2.
int() changes that text into a real number we can do math with. 3.
if number % 2 == 0: checks the remainder when number is divided by 2. - If the remainder is
0, the condition is True and Python runs the line that is indented right under the if. - If not, it jumps to the
else: part and runs the indented line there. Notice how the two
print() statements line up under their respective if and else. That alignment is the indentation telling Python, “These lines belong to these blocks.”Quick checklist for clean indentation
- Consistent width: 4 spaces each level (no tabs unless you set your editor to insert 4 spaces for a tab).
- Never mix spaces and tabs in the same file.
- Indent after a colon (
:) – this includes if, elif, else, for, while, def, class, etc. - Dedent (remove one level) when the block ends.
Try it yourself!
1. Open a new file called
even_odd.py. 2. Copy the code above, paying special attention to the spaces before the
print() lines. 3. Run it with
python even_odd.py and test a few numbers.If you get an IndentationError, look at the line number Python mentions – the spaces are probably off.
Bottom line:
Indentation isn’t a cosmetic detail; it’s the language’s way of understanding your logic. Mastering it early will make every Python concept feel clearer and your code look beautiful. Keep an eye on those four spaces, and you’ll never get lost in a sea of braces again! 🚀
🚀 Shrink your objects, boost attribute look‑ups – unlock the power of
If you’ve ever profiled a long‑running service and saw hundreds of megabytes tied up in plain Python objects, the culprit is probably the default per‑instance
Why it works
* No per‑instance
* Faster attribute access – the lookup becomes a simple offset calculation rather than a hash‑table probe.
* Predictable memory footprint – each instance occupies the same number of bytes, which is great for large collections (think millions of rows or cache entries).
A minimal example
Seeing the difference
Typical output on CPython 3.11 (64‑bit):
The numbers vary by platform, but the trend is clear – slots shave off memory and give a modest speed bump on attribute access.
Common pitfalls & how to avoid them
| Pitfall | Explanation | Fix |
|---|---|---|
| Trying to add a new attribute at runtime |
| Multiple inheritance with slots | Each base class must define its own
| Pickling |
| Dataclasses | Before Python 3.10 you needed a manual workaround. | From Python 3.10:
When to use
* Large homogeneous collections – e.g. a list of millions of coordinates, AST nodes, or cache entries.
* Performance‑critical loops where attribute look‑ups dominate.
* Memory‑constrained environments such as micro‑services or embedded Python (MicroPython supports a variant of slots).
Avoid
A quick recipe for modern code
(continued below 👇)
__slots__ If you’ve ever profiled a long‑running service and saw hundreds of megabytes tied up in plain Python objects, the culprit is probably the default per‑instance
__dict__. Every time you instantiate a class, Python creates a tiny dictionary to store its attributes – handy, but memory‑hungry. __slots__ tells the interpreter: “Hey, this class will only ever have these exact attributes – don’t waste a dict, lay them out in a compact C‑struct instead.” Why it works
* No per‑instance
__dict__ – the object’s memory layout is fixed at class creation, so the interpreter can allocate a static block of memory sized exactly for the declared slots. * Faster attribute access – the lookup becomes a simple offset calculation rather than a hash‑table probe.
* Predictable memory footprint – each instance occupies the same number of bytes, which is great for large collections (think millions of rows or cache entries).
A minimal example
# classic class – each instance carries a __dict__
class Point:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
# slot‑based version – no __dict__ per instance
class PointSlots:
__slots__ = ('x', 'y') # declare the only allowed attributes
def __init__(self, x: float, y: float):
self.x = x
self.y = y
Seeing the difference
import sys, timeit
p = Point(1.0, 2.0)
ps = PointSlots(1.0, 2.0)
print('size with __dict__: ', sys.getsizeof(p.__dict__) + sys.getsizeof(p))
print('size with __slots__: ', sys.getsizeof(ps))
# Rough speed test – attribute read
n = 10_000_000
print('dict read :',
timeit.timeit('p.x', globals={'p': p}, number=n))
print('slots read :',
timeit.timeit('ps.x', globals={'ps': ps}, number=n))
Typical output on CPython 3.11 (64‑bit):
size with __dict__: 72
size with __slots__: 56
dict read : 0.88 s
slots read : 0.62 s
The numbers vary by platform, but the trend is clear – slots shave off memory and give a modest speed bump on attribute access.
Common pitfalls & how to avoid them
| Pitfall | Explanation | Fix |
|---|---|---|
| Trying to add a new attribute at runtime |
obj.new_attr = 5 raises AttributeError because the slot list is closed. | If you need occasional dynamic fields, add '__dict__' to __slots__: __slots__ = ('x', 'y', '__dict__'). || Multiple inheritance with slots | Each base class must define its own
__slots__; otherwise you get a TypeError. | Use a single base that defines all slots, or explicitly combine them: class C(A, B): __slots__ = A.__slots__ + B.__slots__. || Pickling |
__slots__ objects are picklable, but you must ensure the slots are present in __getstate__/__setstate__ if you override them. | Usually no extra work; only needed for custom state handling. || Dataclasses | Before Python 3.10 you needed a manual workaround. | From Python 3.10:
@dataclass(slots=True) creates a dataclass that uses slots automatically. |When to use
__slots__ * Large homogeneous collections – e.g. a list of millions of coordinates, AST nodes, or cache entries.
* Performance‑critical loops where attribute look‑ups dominate.
* Memory‑constrained environments such as micro‑services or embedded Python (MicroPython supports a variant of slots).
Avoid
__slots__ for objects that need a flexible, dynamic attribute set, or when you rely heavily on __dict__‑based introspection (e.g. generic serialization frameworks that expect a dict). A quick recipe for modern code
(continued below 👇)


