@

Python added an operator with nothing to use it on. It exists purely so numeric libraries can spell matrix multiplication without stealing * from element-wise maths.

Arithmetic operatorPython 3.5+
Common call
a @ b
Returns
a matrix product, if the operands implement __matmul__
Replaces
numpy.dot(a, b) and the old a.dot(b) chains
Watch out
int, float, list and str do NOT support it — TypeError every time
aa — Left operand. Must implement __matmul__ — in practice a numpy array or a custom matrix class.type: Any · required @ bb — Right operand. Falls back to its __rmatmul__ if the left operand returns NotImplemented.type: Any · required
→ Any

Operands

NameTypeRequiredDescription
aAnyyesLeft operand. Must implement __matmul__ — in practice a numpy array or a custom matrix class.
bAnyyesRight operand. Falls back to its __rmatmul__ if the left operand returns NotImplemented.

Return value

Any — Whatever __matmul__ returns. No built-in type defines it, so on plain Python values this always raises TypeError.

Examples

1. Built-ins reject it
3 @ 4
Returns
TypeError: unsupported operand type(s) for @: 'int' and 'int'
2. Lists too
[1, 2] @ [3, 4]
Returns
TypeError: unsupported operand type(s) for @: 'list' and 'list'
3. numpy matrices
import numpy as np np.array([[1,2],[3,4]]) @ np.array([[1,0],[0,1]])
Returns
array([[1, 2], [3, 4]])
4. Element-wise is *
np.array([1,2]) * np.array([3,4])
Returns
array([3, 8])
5. Matrix product is @
np.array([1,2]) @ np.array([3,4])
Returns
11 # 1*3 + 2*4
6. Custom class hook
class M: def __matmul__(self, other): return "product"
Returns
M() @ M() gives "product"

Pitfalls

1. Nothing built in supports it
The operator is part of the language but no standard type implements it. Reaching for @ on ints, floats or lists is always a TypeError — this is the single most surprising thing about it.
No implementation
3 @ 4
TypeError: unsupported operand type(s) for @: 'int' and 'int'
Ordinary multiply
3 * 4
12
2. @ and * mean different things in numpy
For arrays, * is element-wise and @ is the matrix product. Swapping them produces an array of the wrong shape rather than an error, so the bug survives until something downstream complains.
Element-wise
np.array([1,2]) * np.array([3,4])
array([3, 8])
Matrix product
np.array([1,2]) @ np.array([3,4])
11
3. It is also a decorator symbol
The same character introduces decorators, but they are unrelated — one is a prefix on a def, the other an infix operator. Searching for "@ in Python" mostly returns decorator results.
Different feature
@decorator
def f(): ...
a decorator, not matmul
Infix is matmul
result = a @ b
the operator
4. Python 3.5 and newer only
Older interpreters treat it as a syntax error at parse time, so the whole module fails to load rather than failing where the operator is used.
Fails on 3.4
a @ b
SyntaxError: invalid syntax
Call dot
a.dot(b)
works on older versions

When to use

Use it
  • Matrix products with numpy, where it reads far better than nested dot calls
  • Custom linear-algebra classes that need multiplication to mean two things
  • Any domain where element-wise and composed products both exist
Reach for something else
  • Ordinary numeric multiplication → *
  • Any built-in type — none implement it
  • Code that must run on Python 3.4 or older

Notes

Complexity
Entirely determined by the implementation; a numpy matrix product is roughly O(n**3)
Return
Whatever __matmul__ produces; no built-in fallback exists
CPython impl
Objects/abstract.c :: PyNumber_MatrixMultiply, dispatching to __matmul__ / __rmatmul__
Memory
Implementation-defined
Thread-safe
Depends on the operand types

FAQ

PEP 465 argued that matrix multiplication is common enough in scientific Python to deserve its own spelling. Without it, libraries had to choose between * meaning element-wise or matrix multiplication — and both conventions already existed, causing constant confusion.

result = (a @ b) @ c      # vs a.dot(b).dot(c)

History

3.5
The @ operator added by PEP 465, with no built-in implementation by design.