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
Connascence in Python: the 9 types of coupling explained

Connascence in Python: the 9 types of coupling explained

Coupling is often treated as a vague notion: “it’s too coupled” says nothing about what to actually change. Connascence provides a precise vocabulary to name the different forms of coupling, compare them, and decide which ones to reduce first. The concept is documented in detail at connascence.io. Three axes to evaluate connascence Every instance of connascence can be analyzed along three axes: Strength: the stronger a connascence, the harder it is to detect and refactor. Degree: an entity coupled to hundreds of others is more problematic than one coupled to two. Locality: two components that are close together (same class, same module) can tolerate stronger forms. At a distance, those same forms become dangerous. The 9 types of connascence Connascence of Name (CoN) Multiple components agree on the name of an entity. This is the weakest and most unavoidable form. ...

May 11, 2026 · 4 min · Anthony

Newsletter

Get new articles delivered straight to your inbox.

No spam. Unsubscribe in one click.