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
数据分析在当今信息时代发挥着重要作用。单因素方差分析(One-Way ANOVA)是一种关键的统计方法,用于比较三个或更多独立样本组 ...
2025-04-25CDA持证人简介: 居瑜 ,CDA一级持证人国企财务经理,13年财务管理运营经验,在数据分析就业和实践经验方面有着丰富的积累和经 ...
2025-04-25在当今数字化时代,数据分析师的重要性与日俱增。但许多人在踏上这条职业道路时,往往充满疑惑: 如何成为一名数据分析师?成为 ...
2025-04-24以下的文章内容来源于刘静老师的专栏,如果您想阅读专栏《刘静:10大业务分析模型突破业务瓶颈》,点击下方链接 https://edu.cda ...
2025-04-23大咖简介: 刘凯,CDA大咖汇特邀讲师,DAMA中国分会理事,香港金管局特聘数据管理专家,拥有丰富的行业经验。本文将从数据要素 ...
2025-04-22CDA持证人简介 刘伟,美国 NAU 大学计算机信息技术硕士, CDA数据分析师三级持证人,现任职于江苏宝应农商银行数据治理岗。 学 ...
2025-04-21持证人简介:贺渲雯 ,CDA 数据分析师一级持证人,互联网行业数据分析师 今天我将为大家带来一个关于用户私域用户质量数据分析 ...
2025-04-18一、CDA持证人介绍 在数字化浪潮席卷商业领域的当下,数据分析已成为企业发展的关键驱动力。为助力大家深入了解数据分析在电商行 ...
2025-04-17CDA持证人简介:居瑜 ,CDA一级持证人,国企财务经理,13年财务管理运营经验,在数据分析实践方面积累了丰富的行业经验。 一、 ...
2025-04-16持证人简介: CDA持证人刘凌峰,CDA L1持证人,微软认证讲师(MCT)金山办公最有价值专家(KVP),工信部高级项目管理师,拥有 ...
2025-04-15持证人简介:CDA持证人黄葛英,ICF国际教练联盟认证教练,前字节跳动销售主管,拥有丰富的行业经验。在实际生活中,我们可能会 ...
2025-04-14在 Python 编程学习与实践中,Anaconda 是一款极为重要的工具。它作为一个开源的 Python 发行版本,集成了众多常用的科学计算库 ...
2025-04-14随着大数据时代的深入发展,数据运营成为企业不可或缺的岗位之一。这个职位的核心是通过收集、整理和分析数据,帮助企业做出科 ...
2025-04-11持证人简介:CDA持证人黄葛英,ICF国际教练联盟认证教练,前字节跳动销售主管,拥有丰富的行业经验。 本次分享我将以教培行业为 ...
2025-04-11近日《2025中国城市长租市场发展蓝皮书》(下称《蓝皮书》)正式发布。《蓝皮书》指出,当前我国城市住房正经历从“增量扩张”向 ...
2025-04-10在数字化时代的浪潮中,数据已经成为企业决策和运营的核心。每一位客户,每一次交易,都承载着丰富的信息和价值。 如何在海量客 ...
2025-04-09数据是数字化的基础。随着工业4.0的推进,企业生产运作过程中的在线数据变得更加丰富;而互联网、新零售等C端应用的丰富多彩,产 ...
2025-04-094月7日,美国关税政策对全球金融市场的冲击仍在肆虐,周一亚市早盘,美股股指、原油期货、加密货币、贵金属等资产齐齐重挫,市场 ...
2025-04-08背景 3月26日,科技圈迎来一则重磅消息,苹果公司宣布向浙江大学捐赠 3000 万元人民币,用于支持编程教育。 这一举措并非偶然, ...
2025-04-07在当今数据驱动的时代,数据分析能力备受青睐,数据分析能力频繁出现在岗位需求的描述中,不分岗位的任职要求中,会特意标出“熟 ...
2025-04-03