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

對於 ComplexReal 添加了對實數起作用的操作。

簡而言之,這些是:轉換為 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

當然,還有更多可能的數字 ABC,如果它排除了新增這些 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 的例項,它是 Complex 的子型別(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)

# ...