Python 中的數字究竟是什麼?

花下貓語:在 Python 中,不一樣類型的數字能夠直接作算術運算,並不須要做顯式的類型轉換。可是,它的「隱式類型轉換」可能跟其它語言不一樣,由於 Python 中的數字是一種特殊的對象,派生自同一個抽象基類。在上一篇文章 中,咱們討論到了 Python 數字的運算,而後我想探究「Python 的數字對象究竟是什麼」的話題,因此就翻譯了這篇 PEP,但願對你也有所幫助。html


PEP原文: https://www.python.org/dev/peps/pep-3141/python

PEP標題: PEP 3141 -- A Type Hierarchy for Numbersgit

PEP做者: Jeffrey Yasskingithub

建立日期: 2007-04-23ide

譯者 :豌豆花下貓@Python貓公衆號函數

PEP翻譯計劃: https://github.com/chinesehuazhou/peps-cnui

概要

本提案定義了一種抽象基類(ABC)(PEP 3119)的層次結構,用來表示相似數字(number-like)的類。它提出了一個 Number :> Complex :> Real :> Rational :> Integral 的層次結構,其中 A :> B 表示「A 是 B 的超類」。該層次結構受到了 Scheme 的數字塔(numeric tower)啓發。(譯註:數字--複數--實數--有理數--整數)this

基本原理

以數字做爲參數的函數應該可以斷定這些數字的屬性,而且根據數字的類型,肯定是否以及什麼時候進行重載,即基於參數的類型,函數應該是可重載的。翻譯

例如,切片要求其參數爲Integrals,而math模塊中的函數要求其參數爲Realcode

規範

本 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。

  1. __trunc__(self):在新的內置 trunc(x) 裏調用,它返回從 0 到 x 之間的最接近 x 的 Integral。
  2. __floor__(self):在 math.floor(x) 裏調用,返回最大的 Integral <= x。
  3. __ceil__(self):在 math.ceil(x) 裏調用,返回最小的 Integral > = x。
  4. __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,我這麼考慮:

  1. 若是 A 定義了接受 b 的__add__,那麼沒問題。
  2. 若是 A 走到了樣板代碼分支(譯註:else 分支),還從__add__返回一個值的話,那麼咱們就錯過了爲 B 定義一個更智能的__radd__的可能性,所以樣板應該從__add__返回 NotImplemented。(或者 A 能夠不實現__add__)
  3. 而後 B 的__radd__的機會來了。若是它接受 a,那麼沒問題。
  4. 若是它走到樣板分支上,就沒有辦法了,所以須要有默認的實現。
  5. 若是 B <: A,則 Python 會在 A.__ add__以前嘗試 B.__ radd__。這也能夠,由於它是基於 A 而實現的,所以能夠在委派給 Complex 以前處理這些實例。

若是 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 類型做爲數字塔的一部分。

參考文獻

一、抽象基類簡介:http://www.python.org/dev/peps/pep-3119/

二、多是 Python 3 的類樹?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

(譯註:在譯完以後,我才發現「PEP中文翻譯計劃」已收錄過一篇譯文,有些地方譯得不盡相同,讀者們可比對閱讀。)

致謝

感謝 Neal Norwitz 最初鼓勵我編寫此 PEP,感謝 Travis Oliphant 指出 numpy 社區並不真正關心代數概念,感謝 Alan Isaac 提醒我 Scheme 已經作到了,以及感謝 Guido van Rossum 和郵件組裏的其餘人幫忙完善了這套概念。

版權

該文檔已放入公共領域。

源文件:https://github.com/python/peps/blob/master/pep-3141.txt

相關文章
相關標籤/搜索