numbers — Clase base abstracta numérica

Código fuente: Lib/numbers.py


El módulo numbers (PEP 3141) define una jerarquía de abstract base classes numérico que define progresivamente más operaciones. Ninguno de los tipos definidos en este módulo está destinado a ser instanciado.

class numbers.Number

La raíz de la jerarquía numérica. Si desea validar si un argumento x es un número, sin importar su tipo, use isinstance(x, Number).

La torre numérica

class numbers.Complex

Las subclases de este tipo describen números complejos e incluyen las operaciones que funcionan en el tipo complex integrado. Estos son: conversiones a complex y bool, real, imag, +, -, *, /, **, abs(), conjugate(), == y !=. Todos excepto - y != son abstractos.

real

Abstracto. Recupera el componente real de este número.

imag

Abstracto. Recupera el componente imaginario de este número.

abstractmethod conjugate()

Abstracto. Retorna el complejo conjugado. Por ejemplo, (1+3j).conjugate() == (1-3j).

class numbers.Real

Para Complex, Real agrega las operaciones que trabajan con números reales.

En resumen, estos son: conversiones a float, math.trunc(), round(), math.floor(), math.ceil(), divmod(), //, %, <, <=, >, y >=.

Real también proporciona valores predeterminados para complex(), real, imag, y conjugate().

class numbers.Rational

Subtypes Real and adds numerator and denominator properties. It also provides a default for float().

The numerator and denominator values should be instances of Integral and should be in lowest terms with denominator positive.

numerator

Abstracto.

denominator

Abstracto.

class numbers.Integral

Subtipos Rational y agrega una conversión a int. Proporciona valores predeterminados para float(), numerator y denominator. Agrega métodos abstractos para pow() con operaciones de módulo y cadena de bits: <<, >>, &, ^, |, ~.

Notas para implementadores de tipos

Los implementadores deben tener cuidado de igualar números iguales y aplicar un hash a los mismos valores. Esto puede ser sutil si hay dos extensiones diferentes de los números reales. Por ejemplo, fractions.Fraction implementa hash() de la siguiente manera:

def __hash__(self):
    if self.denominator == 1:
        # Get integers right.
        return hash(self.numerator)
    # Expensive check, but definitely correct.
    if self == float(self):
        return hash(float(self))
    else:
        # Use tuple's hash to avoid a high collision rate on
        # simple fractions.
        return hash((self.numerator, self.denominator))

Agregar más ABCs numéricos

Por supuesto, hay más ABCs posibles para los números, y esto sería una jerarquía deficiente si se excluye la posibilidad de añadirlos. Puede usar MyFoo entre Complex y Real así:

class MyFoo(Complex): ...
MyFoo.register(Real)

Implementar operaciones aritméticas

Queremos implementar las operaciones aritméticas tal que las operaciones de modo mixto llamen a una implementación cuyo autor conocía los tipos de ambos argumentos, o convertir ambos argumentos al tipo incorporado más cercano antes de hacer la operación. Para subtipos de Integral, esto significa que __add__() y __radd__() tienen que ser definidos como:

class MyIntegral(Integral):

    def __add__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(self, other)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(self, other)
        else:
            return NotImplemented

    def __radd__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(other, self)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(other, self)
        elif isinstance(other, Integral):
            return int(other) + int(self)
        elif isinstance(other, Real):
            return float(other) + float(self)
        elif isinstance(other, Complex):
            return complex(other) + complex(self)
        else:
            return NotImplemented

Hay 5 casos diferentes para una operación de tipo mixto en subclases de Complex. Se explicará todo el código anterior que no se refiere a MyIntegral y OtherTypeIKnowAbout` como "repetitivo". ``a será una instancia de A, que es un subtipo de Complex (a: A <: Complex`), y ``b : B <: Complex. Consideraré a + b:

  1. Si A define un __add__() que acepta b, todo está bien.

  2. Si A recurre al código repetitivo y retorna un valor de __add__(), perderíamos la posibilidad de que B defina un __radd __() más inteligente, por lo que el código repetitivo debería retornar NotImplemented de __add__(). (O A no puede implementar __add__() en absoluto.)

  3. Entonces B’s __radd__() tiene una oportunidad. Si acepta a, todo esta bien.

  4. Si se vuelve a caer en el código repetitivo, no hay más posibles métodos para probar, por lo que acá debería vivir la implementación predeterminada.

  5. Si B <: A, Python probara B.__radd__ antes que A.__add__. Esto está bien, porque se implementó con conocimiento de A, por lo que puede manejar instancias antes de delegar un Complex.

Si A <: Complex y B <: Real sin compartir ningún otro conocimiento,la operación compartida apropiada es la que involucra la clase complex incorporada, y ambos __radd__() desencadenan allí, entonces a+b == b+a.

Dado que la mayoría de las operaciones en un tipo determinado serán muy similares, puede ser útil definir una función auxiliar que genere las instancias forward y reverse de cualquier operador dado. Por ejemplo, fractions.Fraction así:

def _operator_fallbacks(monomorphic_operator, fallback_operator):
    def forward(a, b):
        if isinstance(b, (int, Fraction)):
            return monomorphic_operator(a, b)
        elif isinstance(b, float):
            return fallback_operator(float(a), b)
        elif isinstance(b, complex):
            return fallback_operator(complex(a), b)
        else:
            return NotImplemented
    forward.__name__ = '__' + fallback_operator.__name__ + '__'
    forward.__doc__ = monomorphic_operator.__doc__

    def reverse(b, a):
        if isinstance(a, Rational):
            # Includes ints.
            return monomorphic_operator(a, b)
        elif isinstance(a, Real):
            return fallback_operator(float(a), float(b))
        elif isinstance(a, Complex):
            return fallback_operator(complex(a), complex(b))
        else:
            return NotImplemented
    reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
    reverse.__doc__ = monomorphic_operator.__doc__

    return forward, reverse

def _add(a, b):
    """a + b"""
    return Fraction(a.numerator * b.denominator +
                    b.numerator * a.denominator,
                    a.denominator * b.denominator)

__add__, __radd__ = _operator_fallbacks(_add, operator.add)

# ...