• 技术文章 >Python技术 >Python高级

    认识python中的数字

    coldplay.xixicoldplay.xixi2020-09-22 17:15:37转载3227

    概要

    本提案定义了一种抽象基类(ABC)(PEP 3119)的层次结构,用来表示类似数字(number-like)的类。它提出了一个 Number :> Complex :> Real :> Rational :> Integral 的层次结构,其中 A :> B 表示“A 是 B 的超类”。该层次结构受到了 Scheme 的数字塔(numeric tower)启发。(译注:数字--复数--实数--有理数--整数)

    基本原理

    以数字作为参数的函数应该能够判定这些数字的属性,并且根据数字的类型,确定是否以及何时进行重载,即基于参数的类型,函数应该是可重载的。

    例如,切片要求其参数为Integrals,而math模块中的函数要求其参数为Real。

    规范

    本 PEP 规定了一组抽象基类(Abstract Base Class),并提出了一个实现某些方法的通用策略。它使用了来自于PEP 3119的术语,但是该层次结构旨在对特定类集的任何系统方法都有意义。

    标准库中的类型检查应该使用这些类,而不是具体的内置类型。

    数值类

    我们从 Number 类开始,它是人们想象的数字类型的模糊概念。此类仅用于重载;它不提供任何操作。

    class Number(metaclass=ABCMeta): pass

    大多数复数(complex number)的实现都是可散列的,但是如果你需要依赖它,则必须明确地检查:此层次结构支持可变的数。

    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的操作。实数(Real number)是完全有序的,除了 NaN(本 PEP 基本上不考虑它)。

    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

    运算及__magic__方法的变更

    为了支持从 float 到 int(确切地说,从 Real 到 Integral)的精度收缩,我们提出了以下新的 __magic__ 方法,可以从相应的库函数中调用。所有这些方法都返回 Intergral 而不是 Real。

    __trunc__(self):在新的内置 trunc(x) 里调用,它返回从 0 到 x 之间的最接近 x 的 Integral。

    __floor__(self):在 math.floor(x) 里调用,返回 Integral <= x。

    __ceil__(self):在 math.ceil(x) 里调用,返回最小的 Integral > = x。

    __round__(self):在 round(x) 里调用,返回最接近 x 的 Integral ,根据选定的类型作四舍五入。浮点数将从 3.0 版本起改为向偶数端四舍五入。(译注:round(2.5) 等于 2,round(3.5) 等于 4)。它还有一个带两参数的版本__round__(self, ndigits),被 round(x, ndigits) 调用,但返回的是一个 Real。

    在 2.6 版本中,math.floor、math.ceil 和 round 将继续返回浮点数。

    float 的 int() 转换等效于 trunc()。一般而言,int() 的转换首先会尝试__int__(),如果找不到,再尝试__trunc__()。

    complex.__{divmod, mod, floordiv, int, float}__ 也消失了。提供一个好的错误消息来帮助困惑的搬运工会很好,但更重要的是不出现在 help(complex) 中。

    给类型实现者的说明

    实现者应该注意使相等的数字相等,并将它们散列为相同的值。如果实数有两个不同的扩展,这可能会变得微妙。例如,一个复数类型可以像这样合理地实现 hash():

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

    但应注意所有超出了内置复数范围或精度的值。

    添加更多数字抽象基类

    当然,数字还可能有更多的抽象基类,如果排除了添加这些数字的可能性,这会是一个糟糕的等级体系。你可以使用以下方法在 Complex 和 Real 之间添加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 种不同的情况。我把以上所有未包含 MyIntegral 和 OtherTypeIKnowAbout 的代码称为“样板”。

    a 是 A 的实例,它是Complex(a : A <: Complex) 的子类型,还有 b : B <: Complex。对于 a + b,我这么考虑:

    如果 A <: Complex 和 B <: Real 没有其它关系,则合适的共享操作是内置复数的操作,它们的__radd__都在其中,因此 a + b == b + a。(译注:这几段没看太明白,可能译得不对)

    被拒绝的方案

    本 PEP 的初始版本定义了一个被 Haskell Numeric Prelude 所启发的代数层次结构,其中包括 MonoidUnderPlus、AdditiveGroup、Ring 和 Field,并在得到数字之前,还有其它几种可能的代数类型。

    我们原本希望这对使用向量和矩阵的人有用,但 NumPy 社区确实对此并不感兴趣,另外我们还遇到了一个问题,即便 x 是 X <: MonoidUnderPlus 的实例,而且 y 是 Y < : MonoidUnderPlus 的实例,x + y 可能还是行不通。

    然后,我们为数字提供了更多的分支结构,包括高斯整数(Gaussian Integer)和 Z/nZ 之类的东西,它们可以是 Complex,但不一定支持“除”之类的操作。

    社区认为这对 Python 来说太复杂了,因此我现在缩小了提案的范围,使其更接近于 Scheme 数字塔。

    十进制类型

    经与作者协商,已决定目前不将 Decimal 类型作为数字塔的一部分。

    更多相关免费学习推荐:python教程

    专题推荐:python数字
    上一篇:Python大神用9个实用技巧,分享给你 下一篇:python编程:面向对象与过程是什么?

    相关文章推荐

    • Python大神用9个实用技巧,分享给你

    全部评论我要评论

    © 2021 Python学习网 苏ICP备2021003149号-1

  • 取消发布评论
  • 

    Python学习网