• 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 和邮件列表中的很多人帮我完善了概念。

  • 相关阅读:
    NC20282 棘手的操作(启发式合并)
    CF707D Persistent Bookcase(主席树+bitset)
    CF1473E Minimum Path(分层图+最短路)
    线段树优化建图2模板(暂无正确性保证)
    subprocess
    django中update_or_create()
    django中重复键值违反唯一键约束错误
    tox运行报C901错误解决办法
    gitlab搭建
    git命令
  • 原文地址:https://www.cnblogs.com/popapa/p/PEP3141.html
Copyright © 2020-2023  润新知