栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python是如何面向对象的-int的底层实现

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python是如何面向对象的-int的底层实现

python 面向对象

其实大家在刚接触python的时候,听到最多的应该就是“万物皆对象”。而对于对象,大多数都是用 class 去做这方面的演示。当然这肯定是没有错的,但可能会让大家产生 obj = class 的概念;后面和大家一起学习python的基础类型(int、str、list、set、tuple、dict)的底层实现。

python 基础类型 可变对象 & 不可变对象

像大家一开始接触python的时候,不可避免接触到两个概念:“不可变对象”、“可变对象”。
那什么是可变对象,什么是不可变对象?
网上大多是用tuple和list去做这方面的解释。例如在创建某个list对象后,可以更改其内部的值而id不变;创建出来的元组对象无法在不更改id的情况下,更改其内部的不可变参数(注意,这里说的是不可变参数)。
我们可以对比一下list和tuple的属性:

可以看到,只是python在自己的层面,没有向tuple提供类似于list.append的方法;如果去重写tuple的类方法,就可以将其变为一个可变对象。其实list和tuple在C里都是使用数组实现的。
那为什么python需要设置可变对象和不可变对象?
int与str不多赘述,底层就决定了不可变对象的实质。
那么对于tuple的存在,网上通常认为作用:保障数据安全、响应速度快;后面再详细介绍为什么会有这两个特性。
这里主要是想传递一个概念:可变参数和不可变参数只是在python层面赋予的概念;如果有需要,我们是可以构建成适用自己的结构。我们可以借助这个概念去理解结构,但不要误解结构本身就是这样的。

python 中的 int对象

如果大家对低级语言有一些了解的话,应该接触过整数溢出的概念。我们以C为例,int类型在C中一般为4个字节,故其能表示的有符号整数范围在 -(2^32)/2 —— (2^32)/2 -1;当计算的值处于范围之外时,就会有整数溢出的现象发生。如下,当1000000 * 1000000 时就会发生整数溢出的情况。

那,为什么python中不会发生整数溢出的现象呢?如下,当在python中执行10000000000000000000000000*1000000000000000000000时,结果正常输出。

这已经不是32位或者64位的限制范围内了。那我们可以猜想一下, 到底是一个什么结构,才能保证不会发生整数溢出呢?要么一个int类型的对象非常大,要么就是int类型的对象可以追加空间。第一个猜想一定不会被实现,一定不会允许一个int类型给这么消耗资源。那么python中int的空间一定是可追加的。
不妨验证一下int类型在python的大小:

如图,可以看出:int类型随着值的不断增加,其大小也不断增加。基本验证了我们的想法。
大家也会注意到:无论是C或者Java,int类型都是4字节或8字节;为什么python最小的int就占了28字节呢?
我们具体看一下python int类型的源码:

python在实现int类型时,定义了 PyObject_VAR_HEAD 和 digit ob_digit[1] 两个部分。其中 PyObject_VAR_HEAD 存放的是引用计数、多态一些东西,我也不是很清楚,大家可以自行了解;而ob_digit就是整数数组,在这里存放int的具体值。而ob_digit就是一个不断追加的数组,当超过4字节后;python内部自动追加4字节。换而言之,就是空间不够放了再申请一样大的空间呗。
大家现在应该已经有了ob_digit整数数组不断追加扩大的概念;那具体每一块是多大呢?如果4字节也就是4个8位,如果全部做int类型的存放,也就是每一块最大可以放2^(32)-1;只有当超过这个大小才会申请追加新的4字节空间。那么真的是这样吗?

大家可以看见,每一块4字节的空间,仅存放了2^30-1的大小。这个具体是由于16位的unsigned short digit在计算时舍去最高位;也就是16位只使用15位做存储;对应的32位只使用30位做存储。具体ob_digit是如何存放,追加空间,以及是如何计算的(karatsuba算法)大家可以自行了解;这里不做赘述(我不懂哈哈哈)。

python的小整数池以及id的用法

下面给大家分享一下python中,是如何优化int类型的;也顺便展开一些id的用法。

如图,id是用于标识对象的唯一性。大家注意一下第二行 This is guaranteed to be unique among simultaneously existing objects;下面会解释这句话的具体含义。

如图,很明显在int对象小于等于256时,我们拿取的整数都是同一个对象;当int对象大于256时,我们拿取的整数确不是同一个对象。那python是如何实现的?python这么实现的优势在哪里呢?
答案是池。大家应该到这里,都已经接受int在python中本身也是对象的概念了。那当创建、销毁对象时都会带来开销。而对于小整数而言,是我们最经常使用的一种了。在python中,会事先创建好小整数池去优化int对象的创建;这样就可以避免我们在使用小整数时的开销(类似于单例,你使用的永远是创建好的对象;当存在创建好的对象时,是不会去创建新的对象)。一般来说,python会预先创建-5~256在小整数池内(str其实也有这样的概念,后面再说)去供我们使用;原理和扩展大家可以自行了解,可以根据项目情况自行调整池的大小。

最后我们简单介绍一下id,这个也是我在刷stack_overflow时看见的;觉得很有意思,跟大家分享一下。其实这就是对上面 This is guaranteed to be unique among simultaneously existing objects 的一个解释。如图:

我们会惊奇的发现:a的id和b的id并不相等,且a+1也并不等于b+1;但是二者的id是相同的。为什么呢?

如图,我们可以看见在执行 id(a+1) == id(b+1) 时是先拿到对象a、接着拿到对象1、对对象a和对象1执行BINARY_ADD方法返回一个新对象去求id值;id(b+1)也是这样。那产生的新对象,我们假设用c、d去标识;那么c的生命周期在第一次BINARY_ADDCALL_FUNCTION中间、d的生命周期在第二次BINARY_ADDCALL_FUNCTION中间;当c被释放时,其地址被新对象d直接引用;就造成了二者id相等的现象产生。
我们再回过头来看官方对 id 的解释:This is guaranteed to be unique among simultaneously existing objects。也就是说当对象的生命周期没有交集时,id并不能做为唯一性的判断。当然,像python对象的生成、引用等更深层次的需要大家去自己深入研究(没看没看我没看)。

如果文章能为大家在想更深入了解python时,提供一点帮助或者思路;实属幸甚。


本文引用链接:
https://www.jb51.net/article/248346.htm#_lab2_0_0
https://stackoverflow.com/questions/19381492/how-does-c-implements-the-python-assignment-of-large-numbers
https://stackoverflow.com/questions/40009015/how-is-python-storing-strings-so-that-the-is-operator-works-on-literals

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1009476.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号