龙空技术网

7. Python早期设计与开发:数字类型的设计缺陷

ReadingPython 166

前言:

现时你们对“c中长整型怎么定义”大约比较重视,大家都想要了解一些“c中长整型怎么定义”的相关知识。那么小编也在网上汇集了一些对于“c中长整型怎么定义””的相关内容,希望朋友们能喜欢,各位老铁们快快来学习一下吧!

本系列文章译自Python之父 Guido van Rossum 的系列博客“The History of Python”。这个博客系列对我们理解Python及其演变很有帮助,经Guido同意,在这里翻译推荐给大家,希望大家喜欢,也请大家多多指教!

整型与长整型的歧义

在 Python 的数字类型,特别是整型的设计中,我犯了几个比较严重的错误,也吸取了一些教训。

Python 中有两种整型,因此,必须在程序运行时有所区分。我的方案是在长整型后加上字母 L(例如:1234L)。其实这个方案已经违背了 Python 从 ABC 借鉴过来的设计原则——不要让用户关心实现细节。

不过,这还只是小问题,更重要的问题是,Python 中的整型和长整型的底层实现,在一些特定情况下,会出现语义学上的不同。

系统位数整型溢出后,默认会剩下32位,或者是其它C语言中长整型溢出的可能结果,而 Python 中的长整型(大数整型)没有溢出的问题。另外,整型通常是有符号数,但在位操作、移位操作、以及与十进制或十六进制互相转换时,是和无符号数一样的,而 Python 中的长整型则一直按有符号数处理。

于是,歧义出现了:比如说,在32位系统中,1<<31(1左移31位)的结果是最大的负整数,1<<32 的结果是 0,而 1L<<31(长整型的1左移31位)的结果是 231,1L<<32 的结果是 232。

溢出异常

为处理这些问题,我采取了一种简单粗暴的办法:避免整型溢出的默认结果,在溢出时抛出一个异常(OverflowError exception)。(当然,用户自己做位操作的时候,Python 不会进行溢出检查,这种情况下,我假设用户清楚这些操作的结果。)毫无疑问,如果我不做溢出检查,有些用户肯定会写一些依赖溢出结果的代码(就像C语言用户一样),后面再做修复,大家的切换成本就会高很多。

溢出检查看上去只是个很小的实现细节,但我的亲身经历告诉我,这个特性非常有用。

记得当年,为庆祝 ABC 的主要作者 Lambert Meertens 在 CWI 工作25周年,Richard Bird 提出“Meertens数”(Meertens numbers 译注:一个数的自身也是其哥德尔数。娱乐而已,不必认真。)的概念作为礼物。我试着用 Python 实现一个简单的算法来计算Meertens数。最初的几个Meertens数都很小,因此写代码的时候,我没考虑到溢出的情况——经过痛苦而漫长的debug,我才终于意识到这个问题。

因此我决定,之后 Python 中的所有整型操作都要进行溢出检查,如果返回结果不是 C 语言中的长整型,就抛出异常。因为给返回结果分配了新对象,所以这个检查不会造成太大的性能开支。

经验与教训

遗憾的是,我必须承认,抛出异常并不是最好的解决方案。当时我执着于C语言中的一个原则:对某种数字类型的操作,其返回结果必须是该类型。这个原则也导致我犯了另一个比较严重的错误:Python 中整数相除时,返回的也是整数,这个错误会在之后的博文中讨论。

现在看来,如果整型操作产生溢出,我应该返回长整型——这也是 Python 现在的工作方式,只是这个调整来得太慢了。

Python 数字类型的设计中遇到的这些问题,也产生了一条原则:即 Python 中不应该有未定义的返回值——我认为,如果没有正确的返回结果,就应该抛出异常。因此,Python 不会由于一些未定义值造成程序崩溃。这至今依然是 Python 语言本体及其标准库都遵循的重要原则之一。

个人公众号:ReadingPython

标签: #c中长整型怎么定义