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)

# ...