numbers — Numeriska abstrakta basklasser

Källkod: Lib/numbers.py


Modulen numbers (PEP 3141) definierar en hierarki av numeriska abstrakta basklasser som successivt definierar fler operationer. Ingen av de typer som definieras i denna modul är avsedda att instansieras.

class numbers.Number

Roten i den numeriska hierarkin. Om du bara vill kontrollera om ett argument x är ett tal, utan att bry dig om vilken typ, använd isinstance(x, Number).

Det numeriska tornet

class numbers.Complex

Subklasser av denna typ beskriver komplexa tal och innehåller de operationer som fungerar på den inbyggda typen complex. Dessa är: konverteringar till complex och bool, real, imag, +, -, *, /, **, abs(), conjugate(), == och !=. Alla utom - och != är abstrakta.

real

Sammanfattning. Hämtar den reella komponenten i detta tal.

imag

Sammanfattning. Hämtar den imaginära komponenten i detta tal.

abstractmethod conjugate()

Sammanfattning. Returnerar den komplexa konjugaten. Till exempel, (1+3j).conjugate() == (1-3j).

class numbers.Real

Till Complex lägger Real till de operationer som fungerar på reella tal.

I korthet är dessa: en konvertering till float, math.trunc(), round(), math.floor(), math.ceil(), divmod(), //, %, <, <=, > och >=.

Real innehåller också standardvärden för complex(), real, imag och conjugate().

class numbers.Rational

Subtyp av Real och lägger till egenskaperna numerator och denominator. Det ger också en standard för float().

Värdena numerator och denominator ska vara instanser av Integral och ska vara i lägsta termer med denominator positiv.

numerator

Sammanfattning.

denominator

Sammanfattning.

class numbers.Integral

Subtyp av Rational och lägger till en konvertering till int. Tillhandahåller standardvärden för float(), numerator och denominator. Lägger till abstrakta metoder för pow() med modulus- och bitsträngsoperationer: <<, >>, &, ^, |, ~.

Anmärkningar för typimplementerare

Implementatörer bör vara noga med att göra lika tal lika och hasha dem till samma värden. Detta kan vara subtilt om det finns två olika utvidgningar av de reella talen. Till exempel fractions.Fraction implementerar hash() enligt följande:

def __hash__(self):
    if self.denominator == 1:
        # Gör heltal rätt.
        return hash(self.numerator)
    # Dyr kontroll, men definitivt korrekt.
    om self == float(self):
        returnera hash(float(self))
    else:
        # Använd tupels hash för att undvika en hög kollisionsfrekvens på
        # enkla fraktioner.
        return hash((self.numerator, self.denominator))

Lägga till fler numeriska ABC

Det finns naturligtvis fler möjliga ABC för tal, och detta skulle vara en dålig hierarki om den uteslöt möjligheten att lägga till dessa. Du kan lägga till MyFoo mellan Complex och Real med:

klass MyFoo(Komplex): ...
MyFoo.register(Real)

Implementering av aritmetiska operationer

Vi vill implementera de aritmetiska operationerna så att operationer i blandade lägen antingen anropar en implementation vars författare kände till typerna för båda argumenten, eller konverterar båda till närmaste inbyggda typ och utför operationen där. För subtyper av Integral innebär detta att __add__() och __radd__() bör definieras som:

klass 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 Inte implementerad

    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 komplex(annan) + komplex(själv)
        else:
            return Ej implementerad

Det finns 5 olika fall för en mixed-type operation på subklasser av Complex. Jag kommer att referera till all ovanstående kod som inte refererar till MyIntegral och OtherTypeIKnowAbout som ”boilerplate”. a kommer att vara en instans av A, som är en subtyp av Complex (a : A <: Complex), och b : B <: Complex. Jag kommer att överväga a + b:

  1. Om A definierar en __add__() som accepterar b, är allt väl.

  2. Om A faller tillbaka till boilerplate koden, och det skulle returnera ett värde från __add__(), skulle vi missa möjligheten att B definierar en mer intelligent __radd__(), så boilerplate bör returnera NotImplemented från __add__(). (Eller så kanske A inte implementerar __add__`() alls.)

  3. Då får B:s __radd__() en chans. Om den accepterar a är allt bra.

  4. Om den faller tillbaka på standardutformningen finns det inga fler möjliga metoder att prova, så det är här standardimplementeringen ska finnas.

  5. Om B <: A, försöker Python B.__radd__ före A.__add__. Detta är ok, eftersom det implementerades med kunskap om A, så det kan hantera dessa instanser innan det delegeras till Complex`.

Om A <: Complex och B <: Real utan att dela någon annan kunskap, så är den lämpliga delade operationen den som involverar den inbyggda complex`, och båda __radd__`() landar där, så a+b == b+a.

Eftersom de flesta operationer på en viss typ kommer att vara mycket lika kan det vara användbart att definiera en hjälpfunktion som genererar de framåtriktade och bakåtriktade instanserna av en viss operator. Till exempel använder fractions.Fraction:

def _operator_fallbacks(monomorf_operator, fallback_operator):
    def forward(a, b):
        if isinstance(b, (int, Fraktion)):
            return monomorfisk_operator(a, b)
        elif isinstance(b, float):
            return fallback_operator(float(a), b)
        elif isinstance(b, komplex):
            return fallback_operator(komplex(a), b)
        else:
            return Ej implementerad
    forward.__name__ = '__' + fallback_operator.__name__ + '__'
    forward.__doc__ = monomorphic_operator.__doc__

    def reverse(b, a):
        if isinstance(a, Rational):
            # Inkluderar ints.
            return monomorfisk_operator(a, b)
        elif isinstance(a, Real):
            return fallback_operator(float(a), float(b))
        elif isinstance(a, Komplex):
            return fallback_operator(komplex(a), komplex(b))
        else:
            return Inte implementerad
    reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
    reverse.__doc__ = monomorphic_operator.__doc__

    returnera framåt, bakåt

def _add(a, b):
    """a + b"""
    return Fraktion(a.täljare * b.nämnare + b.täljare * a.nämnare
                    b.täljare * a.nämnare,
                    a.nämnare * b.nämnare)

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

# ...