Skip to main content
`Ctrl`+`K`

# Operators

Like most other programming languages Python offers lots of operators to form new data from existing one. Important classes of operators are

- arithmetic operators (`+`, `-`, `*`, `/`,…),

- comparison operators (`==`, `!=`, `<`, `>`,…),

- logical operators (`and`, `or`, `not`,…).

## Operator Precedence

Expressions containing more than one operator are evaluated in well-defined order. Python’s operators can be listed from highest to lowest priority. Operators with identical priority are evaluated from left to right.

Syntax | Operator
--- | ---
`**` | exponentiation
`+`, `-` (unary) | sign
`*`, `/`, `//`, `%` | multiplication, division
`+`, `-` (binary) | addition, substraction
`==`, `!=`, `<`, `>`, `<=`, `>=` | comparison
`not` | logical not
`and` | logical and
`or` | logical or

See Python’s documentation for a complete list of all operators.

## Chained Comparisons

We may write chained comparions like `a < b < c`. Python interprets them as single comparisons connected by `and`, that is, `a < b and b < c`.

Note

Unfortunate expressions like `a < b > c` are allowed, too. This example is equivalent to `a < b and b > c`. In a chain only neighboring operands are compared to each other! There is no comparison between `a` and `c` here.

## Augmented Assignments

For arithmetic binary operators there’s a shortcut for expressions like `a = a + b`. We may write such as `a += b`. The latter is called an augmented assignment. Although the result will look the same, there are two technical differences one should know:

- augmented assignment may work in-place,

- for augmented assignment the assignment target will be evaluated only once.

### In-place Computations

For usual binary operators the Python interpreter calls corresponding dunder methods, like `__add__` for `+`. Augmented assignments have their own dunder methods starting with `i`. So `+=` calls `__iadd__`, for instance. The intention is that `+=` may work in-place, that is, without creating a new object. Of course, this is only possible for mutable objects. If there is no dunder method for augmented assignment the interpreter falls back to the usual binary operator’s dunder method.

Example: The `+` operator applied to two lists concatenates both lists. With `a = a + b` a new list object holding the result is created and then the name `a` is tied to the new object. With `a += b` list `b` is appended to the existing list object referred to by `a`.

    a = [1, 2, 3]
    b = [4, 5, 6]

    print(id(a))

    a = a + b

    print(a)
    print(id(a))

    139941989941376
    [1, 2, 3, 4, 5, 6]
    139941989942272

    a = [1, 2, 3]
    b = [4, 5, 6]

    print(id(a))

    a += b

    print(a)
    print(id(a))

    139941989933632
    [1, 2, 3, 4, 5, 6]
    139941989933632

### Only One Evaluation

If the assignment target is a more complex expression like for list items, the expression will be evaluated twice with usual binary operators, but only once if augmented assignment is used.

Example: In the following code an item of a list shall be incremented by 1. The item’s index is computed by some complex function `get_index` (which for demonstration purposes is very simple here). The two code cells show different implementations, resulting in a different number of calls to `get_index`.

    def get_index():
        print('get_index called')
        return 2

    a = [1, 2, 3, 4]

    a[get_index()] = a[get_index()] + 1

    print(a)

    get_index called
    get_index called
    [1, 2, 4, 4]

    def get_index():
        print('get_index called')
        return 2

    a = [1, 2, 3, 4]

    a[get_index()] += 1

    print(a)

    get_index called
    [1, 2, 4, 4]

Note

If efficiency matters you should prefer augmented assignments. Even `a += b` with integers `a` and `b` is more efficient than `a = a + b`, because the name `a` will be looked up only once in the table mapping names to object IDs.

## Operators as Member Functions

All Python operators, `==` and `+` for instance, simply call a specially named member function of the involved objects, a so called dunder method. Lines 3 and 4 of the following code cell do exactly the same thing:

    a = 5

    b = a + 2
    c = a.__add__(2)

    print(b)
    print(c)

    7
    7

Dunder methods allow to create new object types which can implement all the Python operators themselve. What an operator does depends on the operands’ object type. For instance, `+` applied to numbers is usual addition, but `+` applied to strings is concatenation.

### Dunder Methods for Binary Operators

For binary operators like `+` and `==` there is always the question which of both objects to use for calling the corresponding dunder method. In case of comparisons Python uses the dunder method of the left-hand side operand (up to one minor exception which we don’t discuss here). For arithmetic operations Python always tries the left operand first (again, we omit a minor exception). If it does not have the required dunder method, then Python tries the operand on the right-hand side. If both objects do not have the dunder method, then then interpreter stops with an error.

Binary arithmetic operations might be unsymmetric. Thus, there are two variants of most arithmetic dunder methods: one for applying an operation as the left-hand side operand and one for applying an operation as the right-hand side operand. For addition the methods are called `__add__` and `__radd__`, for multiplication we have `__mul__` and `__rmul__`. Others follow the same scheme.

### Operands of Different Types

Often binary operators shall be applied to objects of different types (adding integer and floating point values, for instance). Even if both objects have the corresponding dunder method, one or both of them could lack code for handling certain object types.

In such a case Python calls the dunder method and the method returns `NotImplemented` to signal that it doesn’t know how to handle the other operand. Then the interpreter tries the dunder method of the other operand. If it returns `NotImplemented`, too, then the interpreter stops with an error. The `__add__` function of integer objects cannot handle `float` objects, but `__add__` of `float` objects can handle integers, for example:

    a = 2
    b = 1.23
    print(a.__add__(b))
    print(b.__add__(a))

    NotImplemented
    3.23

Writing `a + b` in the example above first calls `a.__add__(b)`, which returns `NotImplemented`, then `b.__radd__`. With `b + a` the first call goes to `b.__add__` and no second call (to `a.__radd__`) is required.

An example of beneficial use of Python’s flexible mechanism for customizing operators is discussed in the project on Vector Multiplication.