Python Complex Type: Working with Complex Numbers
python complex type: Learn how to create, manipulate, and convert Python's built-in complex type, including arithmetic, cmath functions, and common pitfalls.
python complex type requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Python's complex type is a built-in numeric type that stores a pair of floating-point values: a real part and an imaginary part. It is the standard way to represent complex numbers in Python, and it supports the arithmetic operations you would expect from mathematics, such as addition, subtraction, multiplication, and division. For developers who work with signal processing, control systems, or scientific simulations, the complex type removes the need to manually manage real and imaginary components as separate variables.
Creating Complex Numbers
You can create a complex number using the complex constructor or the literal syntax with a j suffix. The literal form is the most readable for constants:
z1 = 3 + 4j z2 = complex(3, 4) z3 = complex("3+4j")
All three produce the same value. The j suffix is used instead of i to avoid confusion with a variable named i. The constructor accepts two numeric arguments, or a single string that follows the format real+imagj. When the string form is used, the string must be a valid complex literal; otherwise a ValueError is raised.
Accessing Real and Imaginary Parts
Every complex object exposes .real and .imag properties, which return the corresponding float values. You can also call .conjugate() to get the complex conjugate, which flips the sign of the imaginary part.
z = 3 + 4j print(z.real) # 3.0 print(z.imag) # 4.0 print(z.conjugate()) # (3-4j)
These properties are read-only; you cannot assign to z.real directly. If you need to modify a component, you must create a new complex number.
Arithmetic Operations on Complex Numbers
The complex type supports +, -, *, /, and **. The behavior follows the standard rules of complex arithmetic. For example, multiplication of (a + bj) and (c + dj) yields (ac - bd) + (ad + bc)j. Python handles this internally, so you rarely need to write the formula yourself.
a = 2 + 3j b = 1 - 2j print(a + b) # (3+1j) print(a * b) # (8-1j) print(a / b) # (-0.8+1.4j)
Division uses the conjugate of the denominator to produce a real denominator, which avoids a separate ZeroDivisionError unless the denominator is exactly zero. Note that the result of division is always a complex object, even when the imaginary part happens to be zero.
Using the cmath Module for Complex Functions
The built-in math module functions do not work with complex numbers; they raise a TypeError if you pass a complex argument. For complex-aware versions of these functions, use the cmath module. It provides sqrt, exp, log, sin, cos, and many others that accept and return complex values.
import cmath z = 1 + 1j print(cmath.sqrt(z)) # (1.09868411346781+0.45508986056222733j) print(cmath.exp(z)) # (1.4686939399158851+2.2873552871788423j)
The cmath module also includes phase() and polar() for working with polar coordinates, and rect() to convert back from polar to rectangular form. These are useful when you need magnitude and angle rather than real and imaginary parts.
Converting Between Complex and Other Numeric Types
You can convert a complex number to a float or an integer only if the imaginary part is zero; otherwise a TypeError is raised. The float() and int() constructors accept a complex argument only when z.imag == 0. For general conversion, use abs(z) to get the magnitude as a float, or z.real and z.imag separately.
z = 5 + 0j print(float(z)) # 5.0 print(int(z)) # 5 z2 = 5 + 1j # float(z2) # TypeError: can't convert complex to float
To create a complex number from a float, pass the real and imaginary parts to the constructor. There is no implicit conversion from a tuple or list; you must unpack them manually.
Performance and Memory Considerations
A complex object stores two Python floats, which are each 24 bytes on a 64-bit system, so the total memory overhead is roughly 48 bytes per value, not counting the object header. This is similar to storing two separate floats in a tuple, but the complex type keeps the pair together and provides optimized C-level arithmetic.
For code that performs many operations on real and imaginary pairs, using the complex type avoids the overhead of repeatedly indexing a tuple or list. The arithmetic operations are implemented in C and are generally faster than manual formulas written in pure Python. However, if you only need the real part of a computation, extracting .real after each operation adds a small overhead. In such cases, keeping real and imaginary values as separate floats may be simpler and marginally faster, but it increases the risk of mixing up the order.
The choice depends on the clarity of your algorithm. For most numerical code, the complex type is the right abstraction because it makes the intent explicit and reduces the chance of accidentally using the wrong component.
Common Pitfalls with Complex Numbers
One frequent mistake is trying to compare complex numbers with < or >. Python does not define a total ordering for complex values, so these comparisons raise a TypeError. You can compare for equality with ==, which checks both real and imaginary parts exactly. For approximate equality, use abs(a - b) < tolerance.
Another pitfall is assuming that abs(z) returns the real part. It returns the magnitude, which is the square root of real**2 + imag**2. If you need the real part, use .real.
Finally, be aware that the j suffix is case-sensitive; 3+4J is also valid, but 3+4i is not. The string form passed to complex() must not contain spaces around the + sign, or it will raise a ValueError.
When to Use the Complex Type
Use the complex type when your problem is naturally expressed in terms of complex numbers, such as Fourier transforms, filter design, or quantum state simulation. If you are only storing a pair of unrelated floats, a tuple or a small class may be more appropriate. The complex type is a built-in, so it is always available and works with standard library functions like abs, round, and repr.
For performance-critical loops, consider whether you can vectorize operations using NumPy, which provides complex arrays that are more efficient than a list of complex objects. But for standard Python code, the complex type is the idiomatic choice.