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)

# ...