Python Addition Operator: Syntax and Behavior
python addition operator: A practical guide to the Python addition operator: numeric addition, concatenation, operator overloading, common mistakes, and performance co...
The python addition operator is the + symbol. In Python, it does more than add numbers: its behavior depends entirely on the types of the operands. For numbers, it performs arithmetic addition. For strings, lists, and tuples, it concatenates. For custom classes, it can invoke __add__ or __radd__. Understanding these different behaviors is essential for writing predictable code and avoiding subtle bugs.
Numeric Addition and Type Coercion
When both operands are numbers, + returns their sum. Python supports integers, floats, and complex numbers. If one operand is an integer and the other a float, the result is a float, because Python follows the principle of numeric coercion: the narrower type is converted to the wider type before addition.
print(2 + 3) # 5 print(2 + 3.0) # 5.0 print(2 + 3j) # (2+3j)
This behavior is consistent with the __add__ method of the left operand. For example, int.__add__(2, 3.0) returns a float. If the left operand's __add__ does not know how to handle the right operand, Python tries the right operand's __radd__ method. This is why 3.0 + 2 also works, even though float.__add__ might not accept an integer directly; it converts the integer first.
Concatenation with Strings, Lists, and Tuples
For sequence types, + concatenates two sequences of the same type. This is a common source of confusion for developers coming from statically typed languages where + is reserved for numbers.
print("Hello, " + "world!") # "Hello, world!" print([1, 2] + [3, 4]) # [1, 2, 3, 4] print((1, 2) + (3, 4)) # (1, 2, 3, 4)
The key restriction is that both operands must be of the same type. Mixing types raises a TypeError. For instance, "a" + 1 fails because there is no implicit conversion from integer to string.
This type-specific behavior is defined by the sequence types' __add__ methods. Lists and tuples preserve their order and create a new object; they do not modify either operand. Strings behave similarly, creating a new string object.
Overloading __add__ for Custom Classes
When you define your own class, you can control how it behaves with + by implementing __add__. The method receives the other operand and should return the result. If the left operand's __add__ returns NotImplemented, Python then calls the right operand's __radd__ method, which is useful for commutative operations.
class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): if isinstance(other, Vector): return Vector(self.x + other.x, self.y + other.y) return NotImplemented def __repr__(self): return f"Vector({self.x}, {self.y})" v1 = Vector(1, 2) v2 = Vector(3, 4) print(v1 + v2) # Vector(4, 6)
Here, v1 + v2 calls v1.__add__(v2). If you had written v2 + v1, it would still work because Vector.__add__ handles both operands symmetrically. For non-commutative operations, implement __radd__ to handle the reversed order.
Common Mistakes with the Addition Operator
A frequent error is assuming + will concatenate different sequence types. For example, [1, 2] + (3, 4) raises TypeError because a list and a tuple are distinct types. Similarly, "a" + ["b"] is invalid. The correct approach is to convert one operand explicitly, such as list((3, 4)) or "".join(["a", "b"]).
Another mistake is using + on mutable defaults. If you define a function with a default argument that is a list and then use + inside the function, you may accidentally create a new list instead of mutating the default. This is not an operator-specific issue, but it interacts with how + creates new objects.
A more subtle issue occurs when mixing integers and booleans. In Python, bool is a subclass of int, so True + 1 returns 2. This can be surprising if you intended to treat booleans as flags. While technically valid, it often indicates a logic error.
Performance Considerations for Repeated Addition
Using + in a loop to build a string or list can be inefficient because each operation creates a new object and copies the existing data. For strings, this results in O(n²) time when concatenating n characters one at a time. The idiomatic alternative is str.join() for strings and list.extend() or list comprehensions for lists.
# Inefficient string building result = "" for word in words: result += word + " " # Efficient string building result = " ".join(words)
Similarly, repeatedly using + on lists in a loop creates a new list each time. Using extend() modifies the list in place and avoids the copying overhead. For large data sets, this difference is measurable, though the exact impact depends on the size and frequency of operations.
Operator Precedence and Chaining
The + operator has a defined precedence in Python. It sits below multiplication and exponentiation but above comparison and boolean operators. When chaining additions, Python evaluates from left to right, so a + b + c is (a + b) + c. This matters when the operands are custom objects with side effects in __add__.
a + b + c
If a.__add__(b) returns an object that also supports +, then that result is added to c. For numeric types, this is straightforward. For custom types, the left-to-right evaluation can cause unexpected behavior if __add__ has side effects or depends on state.
When to Use + vs Alternatives
For numbers, + is the natural choice. For sequences, you should consider the context. Concatenating two lists once is fine, but if you need to combine many lists, itertools.chain() or list.extend() may be more readable and efficient. For strings, join() is almost always better when you have a collection of strings.
For custom data types, implementing __add__ is appropriate when the operation is semantically additive, such as combining two vectors or merging two configuration objects. If the operation is not naturally additive, using a named method like merge() is clearer and avoids overloading the operator in a misleading way.
The choice ultimately depends on readability and the expected behavior of the + operator for that type. In Python, the operator is a form of syntactic sugar, and using it appropriately makes code more expressive, but misusing it can make code confusing.