首页 > 编程语言 >Python 中的数字到底是什么?

Python 中的数字到底是什么?

时间:2022-10-14 13:33:38浏览次数:82  
标签:__ return 数字 到底 Python self int other def

花下猫语:在 Python 中,不同类型的数字可以直接做算术运算,并不需要作显式的类型转换。但是,它的“隐式类型转换”可能跟其它语言不同,因为 Python 中的数字是一种特殊的对象,派生自同一个抽象基类。在上一篇​​文章​​ 中,我们讨论到了 Python 数字的运算,然后我想探究“Python 的数字对象到底是什么”的话题,所以就翻译了这篇 PEP,希望对你也有所帮助。


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​

概要

本提案定义了一种抽象基类(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。

  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 类型作为数字塔的一部分。

参考文献

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 和邮件组里的其他人帮忙完善了这套概念。

版权

该文档已放入公共领域。

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



标签:__,return,数字,到底,Python,self,int,other,def
From: https://blog.51cto.com/u_14244765/5756351

相关文章

  • Python 为什么要有 pass 语句?
    本文出自“Python为什么”系列,请查看​​全部文章​​关于Python中的​​pass​​语句,它似乎很简单(只有4个字母),即使是没有任何编程经验的初学者也能很快地掌握它的用法......
  • Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?
    从接触Python时起,我就觉得Python的元组解包(unpacking)挺有意思,非常简洁好用。最显而易见的例子就是多重赋值,即在一条语句中同时给多个变量赋值:>>>x,y=1,2>>>print(......
  • PyCharm 2022.2 发布了,支持最新 Python 3.11 和 PyScript 框架!
    翻译:Python猫原文:​​https://blog.jetbrains.com/pycharm/2022/07/2022-2​​通常而言,使用新潮的或者快速发展的技术,可能会挺有挑战性,你可能得经常阅读文档,才能熟悉新的语......
  • Python 的切片为什么不会索引越界?
    切片(slice)是Python中一种很有特色的特性,在正式开始之前,我们先来复习一下关于切片的知识吧。切片主要用于序列对象中,按照索引区间截取出一段索引的内容。切片的书写形式:[i......
  • 为什么 Python 的 f-string 可以连接字符串与数字?
    本文出自“Python为什么”系列,归档在Github上:​​https://github.com/chinesehuazhou/python-whydo​​毫无疑问,Python是一门强类型语言。强类型语言。强类型语言!(关于强......
  • Python爬虫之requests模块了解
    requests模块知识点:掌握headers参数的使用掌握发送带参数的请求掌握headers中携带cookie掌握cookies参数的使用掌握cookieJar的转换方法掌握超时参数timeout的使用掌......
  • Python爬虫之mongodb介绍和安装
    Mongodb的介绍和安装学习目标了解非关系型数据库的优势了解mongodb的安装1.mongodb的介绍1.1什么是mongodbmongodb是一个功能最丰富的NoSQL非关系数据库。由C++语言......
  • Python爬虫之scrapy_splash组件的使用
    scrapy_splash组件的使用学习目标了解scrapy_splash组件的作用了解scrapy_splash组件的使用1.什么是scrapy_splash?scrapy_splash是scrapy的一个组件scrapy-splash加载js......
  • Python爬虫之http协议复习
    http协议复习知识点掌握http以及https的概念和默认端口掌握爬虫关注的请求头和响应头了解常见的响应状态码理解浏览器和爬虫爬取的区别一提起http协议,大家都会想起它是......
  • 三点共圆的计算代码(Python版本)
    已知三点坐标,计算圆心和半径defcalc_circle_center_and_radius(p1,p2,p3):x1=p1.xy1=p1.yz1=p1.zx2=p2.xy2=p2.yz2=p2......