9.1. numbers - 数字抽象基类

源代码: Lib/numbers.py

numbers模块( PEP 3141)定义了逐渐定义更多操作的数字abstract base classes的层次结构。本模块中定义的任何类型,都不能被实例化。

class numbers.Number

数字层次结构的根。如果只想检查参数x是否为数字,而不考虑什么类型,请使用isinstance(x, Number)

9.1.1.The numeric tower

class numbers.Complex

此类型的子类描述复数,并包括对内建complex类型工作的操作。These are: conversions to complex and bool, real, imag, +, -, *, /, abs(), conjugate(), ==, and !=. -!=之外的都是抽象的。

real

抽象。检索此数的实部。

imag

抽象。检索此数的虚部。

abstractmethod conjugate()

抽象。返回共轭复数。例如,(1 + 3j).conjugate() == (1-3j)

class numbers.Real

ComplexReal添加了对实数起作用的操作。

In short, those are: a conversion to float, math.trunc(), round(), math.floor(), math.ceil(), divmod(), //, %, <, <=, >, and >=.

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

class numbers.Rational

子类型Real并添加numeratordenominator属性,这些属性应为最低值。有了这些,它提供了float()的默认值。

numerator

抽象。

denominator

抽象。

class numbers.Integral

子类型Rational,并向int添加转换。提供float()numeratordenominator的默认值。Adds abstract methods for ** and bit-string operations: <<, >>, &, ^, |, ~.

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.添加更多数字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 will be an instance of A, which is a subtype of Complex (a : A <: Complex), and b : B <: Complex. 我会考虑a + b

  1. If A defines an __add__() which accepts b, all is well.
  2. If A falls back to the boilerplate code, and it were to return a value from __add__(), we’d miss the possibility that B defines a more intelligent __radd__(), so the boilerplate should return NotImplemented from __add__(). (Or A may not implement __add__() at all.)
  3. Then B‘s __radd__() gets a chance. If it accepts a, all is well.
  4. If it falls back to the boilerplate, there are no more possible methods to try, so this is where the default implementation should live.
  5. If B <: A, Python tries B.__radd__ before A.__add__. This is ok, because it was implemented with knowledge of A, so it can handle those instances before delegating to Complex.

If A <: Complex and B <: Real without sharing any other knowledge, then the appropriate shared operation is the one involving the built in complex, and both __radd__() s land there, so a+b == b+a.

由于大多数任何给定类型操作会很相似,因此,它能定义可生成任何给定操作正反向实例的 helper 函数。例如,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)

# ...