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)

# ...