zoukankan      html  css  js  c++  java
  • PEP 3141 数值类型的层次结构 -- Python官方文档译文 [原创]

    PEP 3141 -- 数值类型的层次结构(A Type Hierarchy for Numbers)

    英文原文:https://www.python.org/dev/peps/pep-3141
    采集日期:2020-02-27

    PEP: 3141
    Title: A Type Hierarchy for Numbers
    Author: Jeffrey Yasskin jyasskin@google.com
    Status: Final
    Type: Standards Track
    Created: 23-Apr-2007
    Post-History: 25-Apr-2007, 16-May-2007, 02-Aug-2007

    目录

    摘要(Abstract)


    本提案定义了数值类抽象基类(ABC,Abstract Base Class,PEP 3119)的层次结构(hierarchy)。这里提出了 Number :> Complex :> Real :> Rational :> Integral 的层级,A :> B 意味着“A 是 B 的超类型”。这种层次结构的制定受到了 Scheme 数值类型塔的启发。

    原由(Rationale)


    用数值作参数的函数应能确定这些数值的属性,并且当语言中增加了基于类型的重载时,应能依据参数类型实现函数重载。比如,切片(slice)操作要求参数为整数类型(Integral),而 math 模块中的函数则要求参数为实数类型(Real)。

    规范(Specification)


    本文定义了一组抽象基类,并给出一些方法的通常实现方式。这里用到了 PEP 3119 中的术语,但这种层次结构对于任何用于类定义的系统性解决方案都颇具意义。

    标准库中的类型检查过程应该采用这些类,而不是采用具体(concrete)的内置类型。

    数值类(Numeric Classses)


    下面就从一个 Number 类开始吧,以便大家把数值的类型先模糊掉。该类只是为了便于重载,并不支持任何操作。

        class Number(metaclass=ABCMeta): pass
    

    复数的大多数实现类都是可散列(hashable)的,但如果要绝对可靠,则必须显式地进行检查,验证数值类型的层次结构是否支持可变(mutable)数值。

        class Complex(Number):
            """Complex defines the operations that work on the builtin complex type.
    
            In short, those are: conversion to complex, bool(), .real, .imag,
            +, -, *, /, **, abs(), .conjugate(), ==, and !=.
    
            If it is given heterogenous arguments, and doesn't have special
            knowledge about them, it should fall back to the builtin complex
            type as described below.
            """
    
            @abstractmethod
            def __complex__(self):
                """Return a builtin complex instance."""
    
            def __bool__(self):
                """True if self != 0."""
                return self != 0
    
            @abstractproperty
            def real(self):
                """Retrieve the real component of this number.
    
                This should subclass Real.
                """
                raise NotImplementedError
    
            @abstractproperty
            def imag(self):
                """Retrieve the real component of this number.
    
                This should subclass Real.
                """
                raise NotImplementedError
    
            @abstractmethod
            def __add__(self, other):
                raise NotImplementedError
    
            @abstractmethod
            def __radd__(self, other):
                raise NotImplementedError
    
            @abstractmethod
            def __neg__(self):
                raise NotImplementedError
    
            def __pos__(self):
                """Coerces self to whatever class defines the method."""
                raise NotImplementedError
    
            def __sub__(self, other):
                return self + -other
    
            def __rsub__(self, other):
                return -self + other
    
            @abstractmethod
            def __mul__(self, other):
                raise NotImplementedError
    
            @abstractmethod
            def __rmul__(self, other):
                raise NotImplementedError
    
            @abstractmethod
            def __div__(self, other):
                """a/b; should promote to float or complex when necessary."""
                raise NotImplementedError
    
            @abstractmethod
            def __rdiv__(self, other):
                raise NotImplementedError
    
            @abstractmethod
            def __pow__(self, exponent):
                """a**b; should promote to float or complex when necessary."""
                raise NotImplementedError
    
            @abstractmethod
            def __rpow__(self, base):
                raise NotImplementedError
    
            @abstractmethod
            def __abs__(self):
                """Returns the Real distance from 0."""
                raise NotImplementedError
    
            @abstractmethod
            def conjugate(self):
                """(x+y*i).conjugate() returns (x-y*i)."""
                raise NotImplementedError
    
            @abstractmethod
            def __eq__(self, other):
                raise NotImplementedError
    
            # __ne__ is inherited from object and negates whatever __eq__ does.
    

    实数 Real 的抽象基类表明,数值在层次结构中处于实数的位置,并且支持内置 float 类型的全部操作。除了 NaN(本文基本忽略)之外,实数是完全有序的。

        class Real(Complex):
            """To Complex, Real adds the operations that work on real numbers.
    
            In short, those are: conversion to float, trunc(), math.floor(),
            math.ceil(), round(), divmod(), //, %, <, <=, >, and >=.
    
            Real also provides defaults for some of the derived operations.
            """
    
            # XXX What to do about the __int__ implementation that's
            # currently present on float?  Get rid of it?
    
            @abstractmethod
            def __float__(self):
                """Any Real can be converted to a native float object."""
                raise NotImplementedError
    
            @abstractmethod
            def __trunc__(self):
                """Truncates self to an Integral.
    
                Returns an Integral i such that:
                  * i>=0 iff self>0;
                  * abs(i) <= abs(self);
                  * for any Integral j satisfying the first two conditions,
                    abs(i) >= abs(j) [i.e. i has "maximal" abs among those].
                i.e. "truncate towards 0".
                """
                raise NotImplementedError
    
            @abstractmethod
            def __floor__(self):
                """Finds the greatest Integral <= self."""
                raise NotImplementedError
    
            @abstractmethod
            def __ceil__(self):
                """Finds the least Integral >= self."""
                raise NotImplementedError
    
            @abstractmethod
            def __round__(self, ndigits:Integral=None):
                """Rounds self to ndigits decimal places, defaulting to 0.
    
                If ndigits is omitted or None, returns an Integral,
                otherwise returns a Real, preferably of the same type as
                self. Types may choose which direction to round half. For
                example, float rounds half toward even.
    
                """
                raise NotImplementedError
    
            def __divmod__(self, other):
                """The pair (self // other, self % other).
    
                Sometimes this can be computed faster than the pair of
                operations.
                """
                return (self // other, self % other)
    
            def __rdivmod__(self, other):
                """The pair (self // other, self % other).
    
                Sometimes this can be computed faster than the pair of
                operations.
                """
                return (other // self, other % self)
    
            @abstractmethod
            def __floordiv__(self, other):
                """The floor() of self/other. Integral."""
                raise NotImplementedError
    
            @abstractmethod
            def __rfloordiv__(self, other):
                """The floor() of other/self."""
                raise NotImplementedError
    
            @abstractmethod
            def __mod__(self, other):
                """self % other
    
                See
                https://mail.python.org/pipermail/python-3000/2006-May/001735.html
                and consider using "self/other - trunc(self/other)"
                instead if you're worried about round-off errors.
                """
                raise NotImplementedError
    
            @abstractmethod
            def __rmod__(self, other):
                """other % self"""
                raise NotImplementedError
    
            @abstractmethod
            def __lt__(self, other):
                """< on Reals defines a total ordering, except perhaps for NaN."""
                raise NotImplementedError
    
            @abstractmethod
            def __le__(self, other):
                raise NotImplementedError
    
            # __gt__ and __ge__ are automatically done by reversing the arguments.
            # (But __le__ is not computed as the opposite of __gt__!)
    
            # Concrete implementations of Complex abstract methods.
            # Subclasses may override these, but don't have to.
    
            def __complex__(self):
                return complex(float(self))
    
            @property
            def real(self):
                return +self
    
            @property
            def imag(self):
                return 0
    
            def conjugate(self):
                """Conjugate is a no-op for Reals."""
                return +self
    

    应把 Demo/classes/Rat.py 清除掉,将其升级为标准库中的 rational.py。这样就能实现有理数的抽象基类 Rational 了。

        class Rational(Real, Exact):
            """.numerator and .denominator should be in lowest terms."""
    
            @abstractproperty
            def numerator(self):
                raise NotImplementedError
    
            @abstractproperty
            def denominator(self):
                raise NotImplementedError
    
            # Concrete implementation of Real's conversion to float.
            # (This invokes Integer.__div__().)
    
            def __float__(self):
                return self.numerator / self.denominator
    

    最后是整数类型:

        class Integral(Rational):
            """Integral adds a conversion to int and the bit-string operations."""
    
            @abstractmethod
            def __int__(self):
                raise NotImplementedError
    
            def __index__(self):
                """__index__() exists because float has __int__()."""
                return int(self)
    
            def __lshift__(self, other):
                return int(self) << int(other)
    
            def __rlshift__(self, other):
                return int(other) << int(self)
    
            def __rshift__(self, other):
                return int(self) >> int(other)
    
            def __rrshift__(self, other):
                return int(other) >> int(self)
    
            def __and__(self, other):
                return int(self) & int(other)
    
            def __rand__(self, other):
                return int(other) & int(self)
    
            def __xor__(self, other):
                return int(self) ^ int(other)
    
            def __rxor__(self, other):
                return int(other) ^ int(self)
    
            def __or__(self, other):
                return int(self) | int(other)
    
            def __ror__(self, other):
                return int(other) | int(self)
    
            def __invert__(self):
                return ~int(self)
    
            # Concrete implementations of Rational and Real abstract methods.
            def __float__(self):
                """float(self) == float(int(self))"""
                return float(int(self))
    
            @property
            def numerator(self):
                """Integers are their own numerators."""
                return +self
    
            @property
            def denominator(self):
                """Integers have a denominator of 1."""
                return 1
    

    运算方法和魔法方法的改动(Changes to operations and magic methods)


    为了支持 float 和 int (Real 和 Integral)之间更细微的差别,下面给出一些新的魔法方法,以供相应的库函数调用。这些方法都会返回 Integral 而非 Real。

    1. __trunc__(self),由新的内置方法 trunc(x) 调用,返回 0 和 x 之间离 x 最近的整数。

    2. __floor__(self),由 math.floor(x) 调用,返回 <= x 的最大整数。

    3. __ceil__(self),由 math.ceil(x) 调用,返回 >= x 的最大整数。

    4. __round__(self),由 round(x) 调用,返回离 x 最近的整数,半数取整将依数据类型而定。在 3.0 版中 float 将会修改为半数向偶数取整。这还有一个带两个参数的版本 __round__(self, ndigits),由 round(x, ndigits) 调用,将会返回实数。

    在 2.6 版中,math.floormath.ceilround 将仍旧返回浮点数。

    float 实现的 int() 转换等效于 trunc()。通常 int() 转换应该先尝试 __int__(),若不存在再尝试 __trunc__()

    complex.__{divmod,mod,floordiv,int,float}__ 也消失了。若是能提供一个好的错误信息就完美了,但更重要的是别再出现在 help(complex) 里了。

    实现类型时的注意事项(Notes for type implementors)


    实现时应注意让相等的数值确实相等,并将他们散列为相同值。如果实数有两种不同的扩展实现,就可能有些微妙了。比如,复数类型如下实现 hash() 就较为合理:

            def __hash__(self):
                return hash(complex(self))
    

    但对那些超出内置复数范围或精度的值应该多加小心。

    加入其他数值型抽象基类(Adding More Numeric ABCs)


    当然,数值型还可能会有更多的抽象基类,如果不考虑添加这些类的能力,数值类型的层次结构会很差劲。比如可以在 ComplexReal 之间加入以下 MyFoo

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

    算术运算的实现(Implementing the arithmetic operations)


    在混合运算时,要么调用两个参数类型已知的实现,要么先把两个参数都转换为最接近的内置类型再执行运算,这便是应该实现的算术运算。对于整型的子类型,这意味着 addradd 应该定义如下:

        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
    

    对于复数类的子类,混合运算有五种不同的情况。这里将把上述所有未引用 MyIntegral 和 OtherTypeIKnowAbout 的代码作为“样板”(boilerplate)。a 将会是 A 的实例,而 AComplex 的子类型(a : A <: Complex),同样 b : B <: Complex。于是 a + b 将会被如下处理:

    1. 如果 A 定义了可以接受 b 的 add 方法,万事大吉。
    2. 如果 A 降级(fall back)到采用样板代码,并要由 add 返回结果值,那么就算 B 定义了更明智的 radd 也会被忽略,于是样板代码应该返回 add 得出的 NotImplemented。(或者 A 可能压根儿就不去实现 add
    3. 然后就轮到 B 的 radd。如果能接受 a 则万事大吉。
    4. 如果 B 降级到采用样板代码,因为没有其他方法可供尝试,所以这时会采用默认的实现代码。
    5. 如果 B <: A,Python 会在 A.__add__ 之前先尝试调用 B.__radd__。这种做法没有问题,因为 B 的方法是在了解 A 的情况下实现的,因此它能够在传递给 Complex 之前处理这些实例。

    如果 A<:ComplexB<:Real 不再共用其他信息,那么共用内置 Complex 类型的相关运算方法就是合理的,两者的 radd 都会落到 Complex 中,因此 a+b == b+a

    未被接受的其他提案(Rejected Alternatives)


    在 Number 形成之前,本 PEP 的最初版本曾经定义了一种受 Haskell Numeric Prelude 启发而得的数值类型层次结构,其中包括 MonoidUnderPlus、AdditiveGroup、Ring、Field,以及之前提及的其他几种数值类型。原本是希望这些对使用向量和矩阵的人有用,但是 NumPy 社区确实对此不感兴趣,同时还遇到了一个问题,即便 xX <: MonoidUnderPlus 的实例,y 也是 Y <: MonoidUnderPlus 的实例,但 x + y 仍有可能没有意义。

    于是后来 Number 又增加了更多分支,将高斯整数(Gaussian Integer)和 Z/nZ 之类的数值包含了进去,他们可能属于 Complex 但不一定要支持除法之类的运算。社区认为对于 Python 而言这种做法太复杂了,因此本提案现在缩小了规模,更接近于 Scheme 数值类型塔

    Decimal 类型(The Decimal Type)


    经与作者协商,决定目前不应将 Decimal 类型加入数值类型塔中。

    参考文献(References)


    抽象基类介绍(http://www.python.org/dev/peps/pep-3119/)

    可能的 Python 3K 类树?Bill Janssen 写的 Wiki(http://wiki.python.org/moin/AbstractBaseClasses)

    NumericPrelude:数值类层次结构的实验性替代方案(http://darcs.haskell.org/numericprelude/docs/html/index.html)

    Scheme 数值类型塔(https://groups.csail.mit.edu/mac/ftpdir/scheme-reports/r5rs-html/r5rs_8.html#SEC50)

    致谢(Acknowledgements)


    感谢 Neal Norwitz 第一时间鼓励我写下本 PEP,感谢 Travis Oliphant 指出 Numpy 用户对数(algebraic)的概念真不太在意,感谢 Alan Isaac 提醒我 Scheme 已经完成了本文相关体系的构建,感谢 Guido van Rossum 和邮件列表中的很多人帮我完善了概念。

  • 相关阅读:
    Java实现 LeetCode 56 合并区间
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
  • 原文地址:https://www.cnblogs.com/popapa/p/PEP3141.html
Copyright © 2011-2022 走看看