numbers — 數值抽象基類

原始碼: Lib/numbers.py


numbers 模組(PEP 3141)定義了一個數值抽象基類的層次結構,它們逐步定義了更多的操作。此模組中定義的任何型別都不應被例項化。

class numbers.Number

數值層次結構的根。如果您只想檢查引數 x 是否是一個數字,而不關心具體型別,請使用 isinstance(x, Number)

數值塔

class numbers.Complex

此型別的子類描述複數,幷包含對內建 complex 型別進行操作的運算。這些運算包括:轉換為 complexboolrealimag+-*/**abs()conjugate()==!=。除了 -!= 之外,所有都是抽象的。

real

抽象。檢索此數字的實部。

imag

抽象。檢索此數字的虛部。

abstractmethod conjugate()

抽象。返回複共軛。例如,(1+3j).conjugate() == (1-3j)

class numbers.Real

Complex 的基礎上,Real 添加了對實數進行操作的運算。

簡而言之,這些運算包括:轉換為 floatmath.trunc()round()math.floor()math.ceil()divmod()//%<<=>>=

Real 還為 complex()realimagconjugate() 提供了預設實現。

class numbers.Rational

繼承自 Real 並添加了 numeratordenominator 屬性。它還為 float() 提供了預設實現。

numeratordenominator 的值應為 Integral 的例項,且應為最簡分數形式,並保持 denominator 為正。

numerator

抽象。此有理數的分子。

denominator

抽象。此有理數的分母。

class numbers.Integral

繼承自 Rational 並添加了轉換為 int 的功能。為 float()numeratordenominator 提供了預設實現。添加了帶有模數的 pow() 抽象方法和位字串操作:<<>>&^|~

型別實現者的注意事項

實現者應注意使相等的數字相等,並將其雜湊到相同的值。如果存在實數的兩個不同擴充套件,這可能很微妙。例如,fractions.Fraction 按如下方式實現 hash()

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))

新增更多數值抽象基類

當然,數字可能還有更多的抽象基類 (ABC),如果這個層次結構排除了新增這些類的可能性,那它將是一個糟糕的層次結構。你可以在 ComplexReal 之間新增 MyFoo,方法是

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

實現算術運算

我們希望實現算術運算,以便混合模式運算要麼呼叫其作者瞭解兩個引數型別的實現,要麼將兩者都轉換為最接近的內建型別並在那裡執行操作。對於 Integral 的子型別,這意味著 __add__()__radd__() 應該定義為

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

對於 Complex 子類上的混合型別操作,有 5 種不同的情況。我將所有不提及 MyIntegralOtherTypeIKnowAbout 的上述程式碼稱為“樣板程式碼”。a 將是 A 的一個例項,AComplex 的子型別(a : A <: Complex),而 b : B <: Complex。我將考慮 a + b

  1. 如果 A 定義了一個接受 b__add__(),一切正常。

  2. 如果 A 回退到樣板程式碼,並且它要從 __add__() 返回一個值,我們將錯過 B 定義了一個更智慧的 __radd__() 的可能性,所以樣板程式碼應該從 __add__() 返回 NotImplemented。(或者 A 可能根本不實現 __add__()。)

  3. 然後 B__radd__() 有機會執行。如果它接受 a,一切正常。

  4. 如果它回退到樣板程式碼,就沒有更多可能的方法可以嘗試了,所以這就是預設實現應該存在的地方。

  5. 如果 B <: A,Python 會在 A.__add__ 之前嘗試 B.__radd__。這是可以的,因為它是在瞭解 A 的情況下實現的,因此它可以在委託給 Complex 之前處理這些例項。

如果 A <: ComplexB <: Real 且它們之間沒有任何其他共享知識,那麼適當的共享操作是涉及內建 complex 的操作,並且兩個 __radd__() 都歸結於此,因此 a+b == b+a

由於任何給定型別的大多數操作都非常相似,因此定義一個幫助函式來生成任何給定運算子的正向和反向例項可能很有用。例如,fractions.Fraction 使用

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)

# ...