引言
前面通过几十篇文章,大概把Python的一些比较实用的基础做了一些介绍,学会这些,基本能应付日常的小的需求开发了,写一些小工具,提高工作的处理效率。
接下来,准备开始进入一个新的篇章,也就是Python的面向对象了。
但是,在开始介绍Python面向对象之前,还是需要对变量这个从开始学一直到每次写代码都要用到的概念,再稍微唠叨一番。
因为,很多编程自学者,哪怕写了挺多的代码了,其实对变量还是一知半解的状态。
变量的盒子比喻
有些教材在介绍变量是,经常将变量比喻为盒子,变量的值,为盒子中所保存的内容。这对于静态语言中的普通变量来说,是合适的。但是,对理解面向对象语言中的引用型变量,会造成理解上的困扰。
如果变量是盒子,那以下的代码是不可解释的:
# 如果变量是盒子,现在有一个盒子叫a,这个盒子里面装着[1, 2, 3]
a = [1, 2, 3]
# 有一个新的盒子,叫b,这个盒子里面也装着[1, 2, 3]
b = a
# 目前来说,从print输出来看,盒子的比喻都是没有问题的
print(a)
print(b)
# 但是,当我们修改了b[0]时,盒子的比喻无法解释了
b[0] = 100
print(b)
# 我们只是修改了b盒子的元素,为什么a盒子的也变了
print(a)
执行结果:
变量的标签比喻
既然变量是盒子的比喻,解释不通了,我们对于变量的理解,必须换一个思路去理解。
在Python中一切皆是对象,变量是跟对象进行绑定的,这种绑定关系又是可以改变的,也许把变量理解成贴在对象上的便签纸,也许更加容易理解。
注:标签比喻及对应的图片来源于《流畅的Python》。
垃圾就该待在垃圾箱里
我们在Python代码中,使用任何对象,都是通过变量来实现的。一个对象上有一个变量与之对应,也就是贴了一个标签,在Python中叫做存在一个对该对象的引用。有多个变量与这同一个对象产生了对应,也就是有多个对该对象的引用。
那么,问题来了,如果一个内存中的对象上没有贴任何标签,也就是没有任何变量与之对应,也就是对该对象的引用数为0,那么这个对象我们可以使用吗?
答案显然是否定的,因为我们根本无法找到这个对象,那么这个对象在内存中,占用着存储空间,却没有任何用,那就是内存垃圾了,垃圾就应该待在垃圾箱里。
所以,最基础、最朴素的内存回收就是基于对象的引用计数实现的,当一个对象的引用计数为0,则这个对象被标记为垃圾,在特定的时机,会出发垃圾回收机制,将之从内存中释放。
在Python中,可以通过sys.getrefcount()函数,获取一个对象的引用,但是,需要注意的是:getrefcount()函数返回的计数会比实际引用计数多一个,因为它包含了作为参数传递给getrefcount()函数时创建的临时引用。
import sys
a = [1, 2, 3]
print(sys.getrefcount(a))
b = a
print(sys.getrefcount(a))
c = a
print(sys.getrefcount(a))
# del操作,并不是删除对象,而是解除变量对对象的引用
del c
print(sys.getrefcount(a))
# 重新赋值操作,也会解除对原对象的引用
b = 123
print(sys.getrefcount(a))
执行结果:
通过引用计数,我们可以知道,此前对del操作的理解,其实可能也是有些偏差的。
del操作,只是解除该变量与某个对象对应的引用关系,对象的引用计数会减少,但并不是字面意思上的删除对象。即使当前变量是对某个对象的最后一个引用了,del之后引用计数为0了,也不是立马进行垃圾回收。而是,在恰当的时机,比如定时或者内存空间不足时,才会触发垃圾回收。
而且,垃圾回收并不是必须的(如果内存足够),垃圾回收必须满足的一个质量保证是,不能将还在使用的对象当做垃圾进行回收。
对象的比较
既然变量本身是对对象的引用,两个变量的比较,其本质是对引用对象的比较。
关于比较,在编程中有两种,一种叫同一性比较,一种叫相等性比较。
同一性比较,是判断两个变量是不是对同一个对象的两个引用、两个标签而已,同一性,显然是隐含相等性的。
相等性比较,是判断两个对象的值/内容是否相等。
a = [1, 2, 3]
b = a
c = [1, 2, 3]
# 同一性比较,通过is实现
print(a is b)
print(a is c)
# 相等性比较,通过==实现
print(a == b)
print(a == c)
执行结果:
==进行的相等性判断,其实是Python中的语法糖,本质上是通过__eq__()魔法函数来实现的。所以,如果是自定义的对象,可以通过实现__eq__()函数,来实现自定义相等性的语义判断。
总结
本文将变量与对象的关系,做了进一步的解析,同时稍微提及了关于Python中的对象引用计数与垃圾回收的概念,并对对象的两种比较方式进行了简单的解释说明。
在接下来的文章中,我们将进入Python新的篇章“面向对象”的介绍。