numbers — Classes de base abstraites numériques¶
Code source : 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 base de la hiérarchie numérique. Si vous voulez juste vérifier qu'un argument x est un nombre, peu importe le type, utilisez
isinstance(x, Number).
La tour numérique¶
-
class
numbers.Complex¶ Subclasses of this type describe complex numbers and include the operations that work on the built-in
complextype. These are: conversions tocomplexandbool,real,imag,+,-,*,/,**,abs(),conjugate(),==, and!=. All except-and!=are abstract.-
real¶ Abstrait. Récupère la partie réelle de ce nombre.
-
imag¶ Abstrait. Retrouve la partie imaginaire de ce nombre.
-
abstractmethod
conjugate()¶ Abstrait. Renvoie le complexe conjugué. Par exemple,
(1+3j).conjugate() == (1-3j).
-
-
class
numbers.Real¶ Realajoute les opérations qui fonctionnent sur les nombres réels àComplex.En bref, celles-ci sont : une conversion vers
float,math.trunc(),round(),math.floor(),math.ceil(),divmod(),//,%,<,<=,>et>=.Real fournit également des valeurs par défaut pour
complex(),real,imagetconjugate().
Notes pour implémenter des types¶
Les développeurs doivent veiller à ce que des nombres égaux soient bien égaux lors de comparaisons et à ce qu'ils soient hachés aux mêmes valeurs. Cela peut être subtil s'il y a deux dérivations différentes des nombres réels. Par exemple, fractions.Fraction implémente hash() comme suit :
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))
Ajouter plus d'ABC numériques¶
Il est bien entendu possible de créer davantage d’ABC pour les nombres et cette hiérarchie serait médiocre si elle excluait la possibilité d'en ajouter. Vous pouvez ajouter MyFoo entre Complex et Real ainsi :
class MyFoo(Complex): ...
MyFoo.register(Real)
Implémentation des opérations arithmétiques¶
Nous voulons implémenter les opérations arithmétiques de sorte que les opérations en mode mixte appellent une implémentation dont l'auteur connaît les types des deux arguments, ou convertissent chacun dans le type natif le plus proche et effectuent l'opération sur ces types. Pour les sous-types de Integral, cela signifie que __add__() et __radd__() devraient être définis comme suit :
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
Il existe 5 cas différents pour une opération de type mixte sur des sous-classes de Complex. Nous nous référerons à tout le code ci-dessus qui ne se réfère pas à MyIntegral et OtherTypeIKnowAbout comme "expression générique". a est une instance de A, qui est un sous-type de Complex (a : A <: Complex) et b : B <: Complex. Considérons a + b:
Si
Adéfinit une__add__()qui accepteb, tout va bien.Si
Afait appel au code générique et que celui-ci renvoie une valeur de__add__(), nous manquons la possibilité queBdéfinisse une__radd__()plus intelligent, donc le code générique devrait retournerNotImplementeddans__add__()(ou alorsAne doit pas implémenter__add__()du tout.)Alors
__radd__()deBa une chance. si elle acceptea, tout va bien.Si elle fait appel au code générique, il n'y a plus de méthode possible à essayer, c'est donc ici que l'implémentation par défaut intervient.
Si
B < : A`, Python essaieB.__radd__avantA.__add__. C'est valable parce qu'elle est implémentée avec la connaissance deA, donc elle peut gérer ces instances avant de déléguer àComplex.
Si A <: Complex et B <: Real sans autre information, alors l'opération commune appropriée est celle impliquant complex et les deux __radd__() atterrissent à cet endroit, donc a+b == b+a.
Comme la plupart des opérations sur un type donné seront très similaires, il peut être utile de définir une fonction accessoire qui génère les instances résultantes et inverses d'un opérateur donné. Par exemple, fractions.Fraction utilise :
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)
# ...