AI Skill Report Card

Implementing Numeric Tower

A-85·Sep 5, 2026·Source: Web
14 / 15
Python
from numbers import Integral, Rational, Real, Complex, Number from fractions import Fraction # Check numeric type generically instead of using int/float/complex directly def process(x): if isinstance(x, Integral): return x << 1 # bitwise ops only valid for Integral elif isinstance(x, Rational): return x.numerator / x.denominator elif isinstance(x, Real): return float(x) elif isinstance(x, Complex): return x.real, x.imag raise TypeError(f"{x!r} is not a number") process(3) # Integral process(Fraction(1, 2)) # Rational process(3.14) # Real process(2 + 3j) # Complex
Recommendation
Description could be tightened slightly to lead with the primary use-case trigger rather than a long technical description
Number
 └── Complex
      └── Real
           └── Rational
                └── Integral

Each ABC is a superclass of the one below it. Built-in registrations:

ABCRegistered built-ins
Complexcomplex
Realfloat
Rationalfractions.Fraction
Integralint

int is also Real, Complex, and Number (inherited transitively). No built-in implements Rational except Fraction.

14 / 15

Progress:

  • Step 1: Decide which ABC matches the semantics of your custom type
  • Step 2: Subclass that ABC directly (don't just duck-type)
  • Step 3: Implement all abstract methods required by that ABC (see table below)
  • Step 4: Decide __eq__/__hash__ semantics consistent with numeric equality
  • Step 5: Test isinstance() against the ABC and against mixed-type arithmetic
  • Step 6: If wrapping a third-party type you can't subclass, use ABC.register()

Required abstract methods per ABC

Complex

  • __complex__, real, imag, __add__, __radd__, __neg__, __pos__
  • __mul__, __rmul__, __truediv__, __rtruediv__, __pow__, __rpow__
  • __abs__, __eq__

Real (adds to Complex)

  • __float__, __trunc__, __floor__, __ceil__, __round__
  • __floordiv__, __rfloordiv__, __mod__, __rmod__
  • __lt__, __le__
  • (real/imag become trivial: real = self, imag = 0)

Rational (adds to Real)

  • numerator, denominator (as properties)
  • Default __float__ is provided as numerator / denominator

Integral (adds to Rational)

  • __int__, __index__
  • __pow__ with optional modulo third argument
  • Bitwise ops: __lshift__, __rshift__, __and__, __xor__, __or__, __invert__
  • Default numerator = self, denominator = 1
Recommendation
Example 3 truncates implementation with a comment ('...implement remaining...') which slightly weakens completeness of that example
17 / 20

Example 1: Registering a third-party type without subclassing Input: You have mpmath.mpf or a custom C-extension float-like type you cannot modify to subclass Real. Output:

Python
from numbers import Real Real.register(mpmath.mpf) isinstance(mpmath.mpf(1.5), Real) # True issubclass(mpmath.mpf, Real) # True

Note: register() grants isinstance/issubclass compatibility only — it does NOT provide any of the mixin methods or operator implementations. The type must already implement the needed behavior itself.

Example 2: Choosing the right ABC for a type check Input: Writing a function def scale(x, factor) that should accept any number type but reject non-numeric input, and must support both integer bit-shifting fast paths and general float fallback. Output:

Python
from numbers import Integral, Complex def scale(x, factor): if not isinstance(x, Complex): raise TypeError("x must be numeric") if isinstance(factor, Integral) and factor >= 0: # fast path only valid for non-negative integers return x * (1 << factor) return x * (2 ** factor)

Example 3: Implementing a minimal custom Rational-like type Input: A Money class representing exact cents that should behave like a Rational. Output:

Python
from numbers import Rational from math import gcd class Money(Rational): def __init__(self, cents, denom=1): g = gcd(cents, denom) or 1 self._num = cents // g self._den = denom // g @property def numerator(self): return self._num @property def denominator(self): return self._den def __add__(self, other): return Money(self._num * other.denominator + other.numerator * self._den, self._den * other.denominator) def __radd__(self, other): return self.__add__(other) # ... implement remaining Complex/Real abstract methods ...
Recommendation
Could add a brief note on performance implications of ABC isinstance checks vs duck typing for hot code paths
  • Check against the ABC, never the concrete type. Use isinstance(x, Integral) instead of isinstance(x, int) so numpy ints, Fractions used as integers, etc., aren't rejected.
  • Pick the most specific ABC that matches semantics, not the most permissive. If your type has no meaningful ordering or is inherently complex-valued, stop at Complex — don't force Real.
  • Prefer subclassing over register() when you control the code; subclassing gives you the default mixin implementations (e.g., Rational.__float__) for free. Use register() only for types you can't modify.
  • hash() consistency: numbers that compare equal (e.g. 1 == 1.0 == Fraction(1,1) == (1+0j)) should hash equally where the type supports hashing at all.
  • Implement __eq__ to accept cross-tower comparisons (e.g. your Rational type should equal a plain int or float with the same value), mirroring how Fraction(2,1) == 2.0 works.
  • Registering a type without implementing arithmetic: register() only affects isinstance/issubclass; calling +, -, etc. on the registered type still fails unless those dunder methods actually exist.
  • Subclassing Integral but forgetting __index__: without it, your type can't be used as a sequence index or in range(), bin(), etc.
  • Assuming Rational implies exact base-10 decimal representation — it means exact numerator/denominator representation, not decimal precision (that's decimal.Decimal, which deliberately does NOT register under this tower's Real/Rational due to differing semantics around exactness/precision).
  • Comparing Decimal and float/Fraction via the tower: decimal.Decimal is intentionally not part of the numeric ABC tower (not registered as Real), so isinstance(Decimal("1.5"), Real) is False — don't assume it participates.
  • Forgetting __hash__ when defining __eq__: Python sets __hash__ to None if you override __eq__ without also defining __hash__, silently breaking use in sets/dicts.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
14/15
Examples
17/20
Completeness
19/20
Format
13/15
Conciseness
14/15