numbers
— Clase base abstracta numérica¶
Código fuente: Lib/numbers.py
The numbers
module (PEP 3141) defines a hierarchy of numeric
abstract base classes which progressively define
more operations. None of the types defined in this module are intended to be instantiated.
-
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
¶ Subclasses of this type describe complex numbers and include the operations that work on the built-in
complex
type. These are: conversions tocomplex
andbool
,real
,imag
,+
,-
,*
,/
,**
,abs()
,conjugate()
,==
, and!=
. All except-
and!=
are abstract.-
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
, yconjugate()
.
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
:
Si
A
define un__add__()
que aceptab
, todo está bien.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 retornarNotImplemented
de__add__()
. (OA
no puede implementar__add__()
en absoluto.)Entonces
B
’s__radd__()
tiene una oportunidad. Si aceptaa
, todo esta bien.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.
Si
B <: A
, Python probaraB.__radd__
antes queA.__add__
. Esto está bien, porque se implementó con conocimiento deA
, por lo que puede manejar instancias antes de delegar unComplex
.
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, numbers.Real):
return fallback_operator(float(a), float(b))
elif isinstance(a, numbers.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)
# ...