Skip to main content

Lib/numbers.py

cpython 3.14 @ ab2d84fe1023/Lib/numbers.py

Lib/numbers.py defines the five-level numeric ABC hierarchy specified by PEP 3141. Every abstract method in the tower corresponds to a Python operator or built-in function. Concrete built-in types (int, float, complex) are wired in at the bottom of the file via register() calls rather than through inheritance, so the hierarchy does not appear in their MROs.

Map

SymbolLinesKindNotes
Number37-46ABCRoot; sets __hash__ = None
Complex57-168ABCAdds __complex__, real, imag, arithmetic, conjugate, __eq__
Complex.register(complex)169registrationLinks the builtin complex type
Real172-289ABCAdds __float__, __trunc__, floor/ceil/round, ordering, //, %
Real.register(float)289registrationLinks the builtin float type
Rational292-326ABCAdds numerator/denominator properties; provides __float__ default
Integral328-427ABCAdds __int__, __index__, three-arg pow, bit-shift and bitwise ops
Integral.register(int)427registrationLinks the builtin int type

Reading

The add/radd pattern

Every binary operator in Complex is declared as a pair of abstract methods. The forward method (__add__) handles self + other; the reflected method (__radd__) handles other + self. Both are marked @abstractmethod, so concrete subclasses must implement both or inherit concrete versions.

# CPython: Lib/numbers.py:96 Complex.__add__
@abstractmethod
def __add__(self, other):
"""self + other"""
raise NotImplementedError

@abstractmethod
def __radd__(self, other):
"""other + self"""
raise NotImplementedError

__sub__ and __rsub__ are the one exception: they are provided as concrete defaults that reduce to negation plus addition (lines 116-122), so subclasses only need to implement __neg__ and __add__.

The register() calls and why they are not inheritance

Registering complex, float, and int via ABCMeta.register rather than listing them as subclasses avoids two problems. First, the built-in types already have a fixed MRO determined in C; retrofitting the ABC tower into that MRO would require touching typeobject.c. Second, Decimal satisfies all the abstract methods of Real numerically but must not be registered as a Real because Decimal + float is undefined, and the ABCs promise interoperability.

# CPython: Lib/numbers.py:169 Complex.register
Complex.register(complex)

# CPython: Lib/numbers.py:289 Real.register
Real.register(float)

# CPython: Lib/numbers.py:427 Integral.register
Integral.register(int)

After these calls, isinstance(1, numbers.Integral) returns True even though int.__mro__ contains no ABC class.

Rational and the numerator/denominator contract

Rational adds two abstract properties and overrides __float__ with an integer-division implementation that avoids floating-point overflow on huge numerators:

# CPython: Lib/numbers.py:317 Rational.__float__
def __float__(self):
"""float(self) = self.numerator / self.denominator"""
return int(self.numerator) / int(self.denominator)

The explicit int() casts ensure that a custom Rational subclass whose numerator property returns a non-int Integral still works correctly. fractions.Fraction inherits this default; Integral overrides it again at line 413 with the simpler float(int(self)).

gopy notes

Status: not yet ported.

Planned package path: module/numbers/.

The abstract method stubs are pure Python and straightforward to translate. The key dependency is ABCMeta (from module/abc/) and its register method. The three register() calls at the bottom of the file must run after the built-in types exist, so they belong in a module init function that runs after the core type system is set up. fractions.Fraction registration can be deferred until module/fractions/ is ported.