Python语言之深入Python中引用计数
小职 2020-11-11 来源 : 阅读 1268 评论 0

摘要:本篇深入介绍了Python中的引用计数,希望对Python的学习有所帮助。

本篇深入介绍了Python中的引用计数,希望对Python的学习有所帮助。

Python语言之深入Python中引用计数

在python中的垃圾回收机制主要是以引用计数为主要手段以标记清除和隔代回收机制为辅的手段 。可以对内存中无效数据的自动管理!在这篇文章,带着这个问题来一直往下看:怎么知道一个对象能不能被调用了呢?

 

回顾内存地址

 

Python中的任何变量都有对应的内存引用,也就是内存地址。

 

如果不是容器类型,那么直接引用和赋值,内存地址都是不会的。

 

>>> a = 1

>>> b = 1

>>> id(a)

140709385600544

>>> id(b)

140709385600544

如果在内存中创建了一个list对象(容器),而且对该对象进行了引用。那么b = [1,2]和c = a有什么区别?

 

>>> a = [1,2]

>>> b = [1,2]

>>> id(a)

1966828025736

>>> id(b)

1966828044488

>>> c = a

>>> id(c)

1966828025736

首先在内存1966828025736处创建了一个列表 [1,2],然后定义了一个名为a的变量。b = [1,2]会新开一个内存地址,c = a直接赋值直接引用[1,2]的内存地址。

 

引用计数

 

在一些代码中,如果存在一些变量但是没有用,会造成内存空间,因此叫做垃圾,所以要回收。

 

引用计数也是一种最直观,最简单的垃圾收集技术。原理非常简单,每一个对象都包含了两个头部信息,一个是类型标志符,标识这个对象的类型;另一个是计数器,记录当前指向该对象的引用数目,表示这个对象被多少个变量名所引用。

 

CPython 使用引用计数来管理内存,所有 Python 脚本中创建的实例,都会有一个引用计数,来记录有多少个指针指向它。当引用计数只有 0 时,则会自动释放内存。

 

在Python中通过sys.getrefcount查看引用计数的方法,

 

print(sys.getrefcount())

注意调用getrefcount()函数会临时增加一次引用计数,得到的结果比预期的多一次。

 

比如,下面这个例子中,a 的引用计数是 3,因为有 a、b 和作为参数传递的 getrefcount 这三个地方,都引用了一个空列表。

 

>>> import sys

>>> a = []

>>> b = a

>>> print(sys.getrefcount(a))

3

我们通过一些例子来看下,可以使python对象的引用计数增加或减少的场景。

 

import sys

a = []

# 两次引用,一次来自 a,一次来自 getrefcount

print(sys.getrefcount(a))

def func(a):

    # 四次引用,a,python 的函数调用栈,函数参数,和 getrefcount

    print(sys.getrefcount(a))

 

func(a)

# 两次引用,一次来自 a,一次来自 getrefcount,函数 func 调用已经不存在

print(sys.getrefcount(a))

 

########## 输出 ##########

2

4

2

引用计数是用来记录对象被引用的次数,每当对象被创建或者被引用时将该对象的引用次数加一,当对象的引用被销毁时该对象的引用次数减一,当对象的引用次数减到零时说明程序中已经没有任何对象持有该对象的引用,换言之就是在以后的程序运行中不会再次使用到该对象了,那么其所占用的空间也就可以被释放了了。

 

计数增加和减少

 

下面引用计数增加的场景:

 

对象被创建并赋值给某个变量,比如:a = 'ABC'

变量间的相互引用(相当于变量指向了同一个对象),比如:b=a

变量作为参数传到函数中。比如:ref_method(a),

将对象放到某个容器对象中(列表、元组、字典)。比如:c = [1, a, 'abc']

引用计数减少的场景:

 

当一个变量离开了作用域,比如:函数执行完成时,执行方法前后的引用计数保持不变,这就是因为方法执行完后,对象的引用计数也会减少,如果在方法内打印,则能看到引用计数增加的效果。

对象的引用变量被销毁时,比如del a或者del b。注意如果del a,再去获取a的引用计数会直接报错。

对象被从容器对象中移除,比如:c.remove(a)

直接将整个容器销毁,比如:del c

对象的引用被赋值给其他对象,相当于变量不指向之前的对象,而是指向了一个新的对象,这种情况,引用计数肯定会发生改变。(排除两个对象默认引用计一致的场景)。

import sys

 

def ref_method(str):

    print(sys.getrefcount(str))

    print("我调用了{}".format(str))

    print('方法执行完了')

 

def ref_count():

    # 引用计数增加的场景

    print('测试引用计数增加')

    a = 'A'

    print(sys.getrefcount(a))   

    b = a

    print(sys.getrefcount(a))

    ref_method(a)

    print(sys.getrefcount(a))

    c = [1, a, 'abc']

    print(sys.getrefcount(a))

 

    # 引用计数减少的场景

    print('测试引用计数减少')

    del b

    print(sys.getrefcount(a))

    c.remove(a)

    print(sys.getrefcount(a))

    del c

    print(sys.getrefcount(a))

    a = 783

    print(sys.getrefcount(a))

 

if __name__ == '__main__':

    ref_count()

 

 

########## 输出 ##########

测试引用计数增加

78   #77+1 77在函数中是随机的

79

81

我调用了A

方法执行完了

79

80

测试引用计数减少

79

78

78

4


关注“职坐标在线”(Zhizuobiao_Online)公众号,免费获取最新技术干货教程资源哦

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved