数字—数值抽象 Base Class

源代码: Lib/numbers.py


numbers模块( PEP 3141)定义了数字抽象 Base Class的层次结构,该层次结构逐渐定义了更多操作。此模块中定义的所有类型都无法实例化。

  • 类别 numbers. Number
    • 数字层次结构的根。如果只想检查参数* x *是否为数字,而又不关心哪种类型,请使用isinstance(x, Number)

数字塔

  • 类别 numbers. Complex

    • 此类型的子类描述复数,并包括适用于内置complex类型的操作。它们是:转换为complexboolrealimag+-*/abs()conjugate()==!=-!=以外的所有内容都是抽象的。
  • real

    • 抽象。检索此数字的实部。
  • imag

    • 抽象。检索此数字的虚部。
  • 抽象方法 conjugate()

    • 抽象。返回复数共轭。例如(1+3j).conjugate() == (1-3j)
  • 类别 numbers. Real

    • ComplexReal中添加了对实数有效的运算。

简而言之,它们是:转换为floatmath.trunc()round()math.floor()math.ceil()divmod()//%<<=>>=

Real 还提供complex()realimagconjugate()的默认值。

  • 类别 numbers. Rational

  • numerator

    • Abstract.
  • denominator

    • Abstract.
  • 类别 numbers. Integral

类型实现者的注意事项

实现者应注意使相等的数字相等,并将它们散列为相同的值。如果实数有两个不同的 extensions,这可能是微妙的。例如,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,如果排除了添加这些数字的可能性,这将是一个糟糕的等级体系。您可以使用以下方法在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

Note

  • 如果A定义了接受badd(),那么一切都很好。

  • 如果A返回样板代码,并且要从add()返回一个值,则我们会错过B定义更智能的radd()的可能性,因此样板应该从add()返回NotImplemented。 (或者A可能根本不实现add()。)

  • 然后Bradd()得到了机会。如果它接受a,那么一切都很好。

  • 如果它落在样板上,则没有更多try的方法,因此这是默认实现应存在的地方。

  • 如果B <: A,Python 将在A.__add__之前tryB.__radd__。可以,因为它是passA的知识实现的,因此它可以在委派给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)

# ...