What Is New in Python 1.4
Python 1.4 introduced keyword arguments in function calls, access control via __getattr__/__setattr__, built-in complex number support, and the __repr__ / __str__ distinction. Python 1.4 also refined the exception hierarchy and improved the standard library significantly. It was the first Python version to get serious traction beyond academic and research circles.
| Category | Change | Notes |
|---|---|---|
| Language | Keyword arguments in function calls | Enabled func(name="Alice", age=30) |
| Language | Access to keyword arguments in functions via **kwargs |
The **kw calling convention |
| Language | Built-in complex number type | 3+2j literals; complex(real, imag) |
| Language | __repr__ distinct from __str__ |
Formal separation of developer repr vs. user-facing str |
| Standard Library | Early Tkinter improvements for GUI programming | -- |
| Standard Library | Improved exception hierarchy | Precursor to the normalized hierarchy in later versions |
| Platform | Windows NT support improved | -- |
Key Features in Python 1.4
Keyword Arguments and **kwargs
Python 1.4 added the ability to call functions using keyword arguments (func(name="Alice")) and to accept an arbitrary keyword argument dictionary with **kwargs. These features enabled more flexible and readable APIs and are still cornerstones of Python's function calling convention.
def configure(host, port=8080, **options):
print(f"host={host}, port={port}, extras={options}")
configure("localhost", port=9090, debug=True, timeout=30)
# host=localhost, port=9090, extras={'debug': True, 'timeout': 30}
Complex Number Type
Python 1.4 added a built-in complex type with literal syntax 3+2j (note: j not i, to avoid confusion with the identifier i). Complex numbers support all arithmetic operations and have .real and .imag attributes.
z1 = 3 + 2j
z2 = 1 - 1j
print(z1 + z2) # (4+1j)
print(z1 * z2) # (5-1j)
print(z1.real) # 3.0
print(z1.imag) # 2.0
print(abs(z1)) # 3.605... (modulus)
__repr__ vs __str__
Python 1.4 formalized the distinction: __repr__ should return an unambiguous, ideally eval-able representation for developers; __str__ should return a human-readable representation. When only __repr__ is defined, Python falls back to it for str(). The convention is: if repr output is valid Python, then eval(repr(obj)) == obj.
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"Point({self.x!r}, {self.y!r})"
def __str__(self):
return f"({self.x}, {self.y})"
p = Point(1, 2)
print(repr(p)) # Point(1, 2) -- developer view
print(str(p)) # (1, 2) -- user view
FAQ
When should **kwargs be used vs explicit keyword parameters?
Use explicit keyword parameters when you know the parameter names and want type-checker support and clear documentation. Use **kwargs for pass-through wrappers, decorators that need to forward arguments, and configuration-style functions where the set of options is open-ended. Avoid **kwargs in APIs where discoverability matters -- it effectively hides the accepted parameters.
Why does Python use j instead of i for imaginary numbers?
In engineering and signal processing, j is the standard notation for the imaginary unit (because i is already used for current in electrical engineering). Python chose j to align with engineering convention. In mathematics, i is common, but Python prioritizes the engineering use case where complex numbers are most practically applied.
Should __repr__ always return eval-able output?
It is a good goal but not always practical. The Python docs say it should be "unambiguous" and "if at all possible, should look like a valid Python expression." For objects with complex state or external resources, a non-eval-able but informative <ClassName ...> form is acceptable. The key is that repr should give a developer enough information to understand the object's state.
Were keyword arguments available before Python 1.4?
No. Before 1.4, all function arguments had to be positional. Python 1.4 added both the calling convention (func(name="value")) and the parameter capture (**kwargs) simultaneously. This was a significant API design unlock -- many Python idioms like dict(key=value) and open(file, encoding="utf-8") depend on it.
Is the complex type useful outside scientific computing?
Occasionally. Besides scientific and engineering use cases, complex numbers appear in signal processing (FFT), rotation mathematics, and some algorithm problems that model 2D geometry in the complex plane. For ordinary application development, floating-point or integer arithmetic is sufficient.