Python itertools: building lazy iterator pipelines

Python itertools: building lazy iterator pipelines

itertools is a standard library module that exposes composable iteration building blocks. Its value is not in replacing a for loop with a cryptically named function, but in processing data streams without ever loading them fully into memory. Every function returns a lazy iterator: nothing is computed until you consume the result. That is what lets you chain transformations over millions of elements with a constant memory footprint. Here are the functions I actually use, grouped by purpose, along with the pitfalls that waste time. ...

May 29, 2026 · 7 min · Anthony
Python collections : Counter, defaultdict, deque and the rest

Python collections : Counter, defaultdict, deque and the rest

The Python collections module has been part of the standard library since version 2.4. It provides specialized data structures that solve recurring problems without any external dependency. Yet many developers keep writing counting loops, conditional key initialization, or Point classes with x, y, z fields when Counter, defaultdict, and namedtuple do exactly that, better and more readably. Here are the six structures I use regularly, with the cases where they actually make a difference. ...

May 22, 2026 · 7 min · Anthony
Python shutil: copy, move and archive files without subprocess

Python shutil: copy, move and archive files without subprocess

When you need to copy a directory, move files, or create an archive in Python, the temptation is to reach for subprocess.run(["cp", "-r", ...]) or os.system("mv ..."). That approach is fragile, non-portable, and unnecessary: shutil (shell utilities) has been in the Python standard library since version 2.3 and handles all of this cleanly. The Python shutil library is the go-to tool for high-level filesystem operations. Why shutil instead of os or subprocess os exposes low-level system calls: rename, link, create directories. It does not copy file contents. os.rename() fails when the source and destination are on different filesystems (separate partitions, mounted Docker volumes, etc.). subprocess with cp or mv does not work on Windows. ...

May 21, 2026 · 4 min · Anthony
Python operator: itemgetter, attrgetter and the art of replacing lambdas

Python operator: itemgetter, attrgetter and the art of replacing lambdas

The operator library has been part of Python’s standard library forever, and yet many developers keep writing lambda x: x[0] or lambda obj: obj.name when an operator function would do the same job, faster and more readably. Understanding what this library offers, and how it is implemented, changes the way you write functional code in Python. What operator contains operator exposes functions that match the language’s operators. operator.add(2, 3) is the functional equivalent of 2 + 3, operator.lt(a, b) corresponds to a < b. The point is not to replace operators in ordinary arithmetic code, that would be absurd. The point is being able to pass an operation as an argument to a higher-order function (map, filter, sorted, reduce, functools.partial). ...

May 20, 2026 · 7 min · Anthony
Python dataclasses: field(default_factory) in depth

Python dataclasses: field(default_factory) in depth

Python dataclasses automatically generate __init__, __repr__, and __eq__ from type annotations. The moment an attribute needs a mutable default value (list, dict, set), you hit a fundamental Python pitfall. field(default_factory=...) is the solution, and understanding why it is necessary changes how you reason about initialization in Python. The mutable default value trap In Python, default parameter values are evaluated once at function definition time, not at each call. This is a language property, not a bug. ...

May 19, 2026 · 5 min · Anthony
Python __add__ and __iadd__: copy or in-place mutation

Python __add__ and __iadd__: copy or in-place mutation

__add__ and __iadd__ define two distinct behaviors for addition in Python: one creates a new object, the other modifies the existing one. This distinction has real consequences for aliases and shared references, and it catches even experienced developers off guard. add: the addition that creates __add__ is called by the + operator. It must return a new object and leave the operands unchanged. class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) a = Vector(1, 2) b = Vector(3, 4) c = a + b # new Vector(4, 6) print(a.x, a.y) # 1 2 — a is unchanged print(id(a) == id(c)) # False — distinct objects a + b calls a.__add__(b). If __add__ is not defined on a or returns NotImplemented, Python tries b.__radd__(a). ...

May 15, 2026 · 4 min · Anthony
Python @property: from encapsulation to descriptors

Python @property: from encapsulation to descriptors

Accessing r.width and writing r.width = 15 with the same syntax as a plain attribute, while running validation or computation under the hood: that is what @property provides. And when that logic needs to be shared across multiple classes, descriptors come into play. @property: getters and setters without friction @property lets you expose a computed or validated attribute with the same syntax as a regular one. class Rectangle: def __init__(self, width: float, height: float): self._width = width self._height = height @property def width(self) -> float: return self._width @width.setter def width(self, value: float) -> None: if value <= 0: raise ValueError("Width must be positive.") self._width = value @property def area(self) -> float: return self._width * self._height The concrete benefit: the public API does not change. Adding validation to an existing attribute breaks no calling code. @property without a setter creates a read-only attribute. @name.deleter handles deletion via del. ...

May 12, 2026 · 3 min · Anthony
Python __slots__: cut instance memory by 40–60% without changing your logic

Python __slots__: cut instance memory by 40–60% without changing your logic

By default, Python allocates a __dict__ for every instance of a class. Flexible, yes. Cheap, no. When you hold thousands or millions of objects in memory at once, that dictionary overhead adds up fast. __slots__ removes it and replaces per-instance storage with compact internal descriptors. Typical result: 40 to 60 percent less memory per instance. What Python does without slots Without any declaration, each instance carries its own __dict__: class Point: def __init__(self, x: float, y: float) -> None: self.x = x self.y = y p = Point(1.0, 2.0) print(p.__dict__) # {'x': 1.0, 'y': 2.0} This dictionary allows adding attributes at runtime: ...

May 6, 2026 · 4 min · Anthony

Newsletter

Get new articles delivered straight to your inbox.

No spam. Unsubscribe in one click.