Skip to main content

9.1. numbers —数值抽象基类

源代码: Lib/numbers.py


numbers 模块(PEP 3141)定义了逐渐定义更多操作的数字 抽象基类 的层次结构。此模块中定义的类型不能实例化。

class numbers.Number

数字层次结构的根。如果你只是想检查一个参数 x 是一个数字,没有关心什么类型,使用 isinstance(x, Number)

9.1.1. 数字塔

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() 的默认值。

numerator

抽象。

denominator

抽象。

class numbers.Integral

子类型 Rational 并将转换添加到 int。提供 float()numeratordenominator 的默认值。为 ** 和位串操作添加抽象方法:<<>>&^|~

9.1.2. 类型实现器的注释

实现者应该小心使等号相等,并将它们哈希为相同的值。如果有两个不同的实数扩展,这可能是微妙的。例如,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))

9.1.2.1. 添加更多数字ABC

当然,对于数字,有更多可能的ABCs,如果它排除了添加这些的可能性,这将是一个糟糕的层次结构。您可以在 ComplexReal 之间添加 MyFoo:

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

9.1.2.2. 实现算术运算

我们要实现算术运算,以便混合模式操作调用一个实现,其作者知道两个参数的类型,或者将两者转换为最近的内置类型并在那里执行操作。对于 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 的实例,其是 Complexa : 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, 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)

# ...