
PEP原文: https://www.python.org/dev/peps/pep-3141
PEP標題: PEP 3141 -- A Type Hierarchy for Numbers
PEP作者: Jeffrey Yasskin
創建日期: 2007-04-23
譯者 :豌豆花下貓
來源:Python貓
PEP翻譯計劃: https://github.com/chinesehuazhou/peps-cn
花下貓語:在python 中,不同類型的數字可以直接做算術運算,并不需要作顯式的類型轉換。但是,它的“隱式類型轉換”可能跟其它語言不同,因為 python 中的數字是一種特殊的對象,派生自同一個抽象基類。在上一篇文章 中,我們討論到了 Python 數字的運算,然后我想探究“Python 的數字對象到底是什么”的話題,所以就翻譯了這篇 PEP,希望對你也有所幫助。
概要
本提案定義了一種抽象基類(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。
在 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 類型作為數字塔的一部分。
參考文獻
1、抽象基類簡介:http://www.python.org/dev/peps/pep-3119/
2、可能是 Python 3 的類樹?Bill Janssen 的 Wiki 頁面:http://wiki.python.org/moin/AbstractBaseClasses
3、NumericPrelude:數字類型類的實驗性備選層次結構:http://darcs.haskell.org/numericprelude/docs/html/index.html
4、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 和郵件組里的其他人幫忙完善了這套概念。
數據分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
解碼數據基因:從數字敏感度到邏輯思維 每當看到超市貨架上商品的排列變化,你是否會聯想到背后的銷售數據波動?三年前在零售行 ...
2025-05-23在本文中,我們將探討 AI 為何能夠加速數據分析、如何在每個步驟中實現數據分析自動化以及使用哪些工具。 數據分析中的AI是什么 ...
2025-05-20當數據遇見人生:我的第一個分析項目 記得三年前接手第一個數據分析項目時,我面對Excel里密密麻麻的銷售數據手足無措。那些跳動 ...
2025-05-20在數字化運營的時代,企業每天都在產生海量數據:用戶點擊行為、商品銷售記錄、廣告投放反饋…… 這些數據就像散落的拼圖,而相 ...
2025-05-19在當今數字化營銷時代,小紅書作為國內領先的社交電商平臺,其銷售數據蘊含著巨大的商業價值。通過對小紅書銷售數據的深入分析, ...
2025-05-16Excel作為最常用的數據分析工具,有沒有什么工具可以幫助我們快速地使用excel表格,只要輕松幾步甚至輸入幾項指令就能搞定呢? ...
2025-05-15數據,如同無形的燃料,驅動著現代社會的運轉。從全球互聯網用戶每天產生的2.5億TB數據,到制造業的傳感器、金融交易 ...
2025-05-15大數據是什么_數據分析師培訓 其實,現在的大數據指的并不僅僅是海量數據,更準確而言是對大數據分析的方法。傳統的數 ...
2025-05-14CDA持證人簡介: 萬木,CDA L1持證人,某電商中廠BI工程師 ,5年數據經驗1年BI內訓師,高級數據分析師,擁有豐富的行業經驗。 ...
2025-05-13CDA持證人簡介: 王明月 ,CDA 數據分析師二級持證人,2年數據產品工作經驗,管理學博士在讀。 學習入口:https://edu.cda.cn/g ...
2025-05-12CDA持證人簡介: 楊貞璽 ,CDA一級持證人,鄭州大學情報學碩士研究生,某上市公司數據分析師。 學習入口:https://edu.cda.cn/g ...
2025-05-09CDA持證人簡介 程靖 CDA會員大咖,暢銷書《小白學產品》作者,13年頂級互聯網公司產品經理相關經驗,曾在百度、美團、阿里等 ...
2025-05-07相信很多做數據分析的小伙伴,都接到過一些高階的數據分析需求,實現的過程需要用到一些數據獲取,數據清洗轉換,建模方法等,這 ...
2025-05-06以下的文章內容來源于劉靜老師的專欄,如果您想閱讀專欄《10大業務分析模型突破業務瓶頸》,點擊下方鏈接 https://edu.cda.cn/g ...
2025-04-30CDA持證人簡介: 邱立峰 CDA 數據分析師二級持證人,數字化轉型專家,數據治理專家,高級數據分析師,擁有豐富的行業經驗。 ...
2025-04-29CDA持證人簡介: 程靖 CDA會員大咖,暢銷書《小白學產品》作者,13年頂級互聯網公司產品經理相關經驗,曾在百度,美團,阿里等 ...
2025-04-28CDA持證人簡介: 居瑜 ,CDA一級持證人國企財務經理,13年財務管理運營經驗,在數據分析就業和實踐經驗方面有著豐富的積累和經 ...
2025-04-27數據分析在當今信息時代發揮著重要作用。單因素方差分析(One-Way ANOVA)是一種關鍵的統計方法,用于比較三個或更多獨立樣本組 ...
2025-04-25CDA持證人簡介: 居瑜 ,CDA一級持證人國企財務經理,13年財務管理運營經驗,在數據分析就業和實踐經驗方面有著豐富的積累和經 ...
2025-04-25在當今數字化時代,數據分析師的重要性與日俱增。但許多人在踏上這條職業道路時,往往充滿疑惑: 如何成為一名數據分析師?成為 ...
2025-04-24