python自学日记32——基础查漏补缺(2)
跟着各种教程学了很久,发现很多基础的问题被问到还是答不上来,然后决定把基础中漏掉的觉得有用的内容补一下。
首先做个测验:
#1. 思考下面三条语句,他们会改变A打印出的值吗?
A = 'spam'
B = A
B = 'shrubbery'
#2. 思考下面三条语句,他们会改变A打印出的值吗?
A = ['spam']
B = A
B[0] = 'shrubbery'
#3. 这样呢?
A = ['spam']
B = A[:]
B[0] = 'shrubbery'
1.动态类型
python不需要提前声明变量和变量类型,只需要给变量赋值就完成了变量的创建、声明,虽然声明这个概念并不存在python语言中。
举个例子,我们在python中运行赋值语句S=3时,python语言完成了以下三步操作:
1.创建一个对象来代表3;
2.创建一个变量S,如果它还没有被创建的话;
3.将变量与新的对象3相连接。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyUc1Y19-1589810824800)(https://s1.ax1x.com/2020/05/18/YhBCWj.png)]
实际效果如上图所示,在运行S=3后,变量S变成了对象3的一个引用,在内部,变量事实上是到对象内存空间的一个指针。
这个图对于下面一句话的理解很关键
2.类型属于对象,而不是变量
举个例子:
a = 10 #这是一个整数
a = 'hello world' #现在它又变成了一个字符串
a = 2.34 #现在是浮点数
以前我理解的a被赋值10,那么a就是整数类型,按照我以前的理解就是我上面注释的情况,事实上并不是这样,从上面的图可以看出,类型是属于对象而不是变量名,我只是把a先后修改为对不同对象的引用而已(如下图)。实际上,python的变量就是在特定的时间引用了一个特定的对象。
3.对象的垃圾回收
如同上面图中所写的,我们把变量a赋值给了不同类型的对象,但是当重新给变量a赋值时,它之前引用的对象占用的空间就会被回收(如果它没有被其他的变量名或对象所引用的话)。这种自动回收对象空间的技术叫做垃圾收集。
在内部,python给每个对象添加了一个计数器,记录当前指向该对象的引用变量的数目,一旦(并精确在同一时间)这个计数器被设置为零,这个对象的内存空间就会自动回收。垃圾收集最直接的、可感受到的好处就是,可以在脚本中任意使用对象而不需要考虑释放内存空间,与C和C++等语言相比省去了大量的基础代码。
4.共享引用
举个例子:
a = 10
b = a
这时变量a和b引用的是同一对象10,二者指向的是同一个对象的内存空间。
如果此时修改变量a
a = 20
那么变量a引用了新的对象20,但是b还是引用的是10,这个赋值运算仅仅改变了变量a,变量b并没有发生改变。结合上面图看,给一个变量赋一个新的值,并不是替换了原始的对象,而是让这个变量去引用完全不同的对象。实际的效果就是对一个变量赋值,仅仅会影响那个被赋值的变量。
但是当可变的对象一级远处的改变进入这个场景,那么这个情形会有某种改变。例如
L1 = [1, 2, 3]
L2 = L1
因为L1和L2指向的是同一个可变对象,如果修改了这个可变对象
L1[0] = 5
L1
输出结果:[5, 2, 3]
L2
输出结果:[5, 2, 3]
在这里我们没有改变L1的引用,改变的是引用对象的一个元素,因为L2共享这个引用,所以对L2也产生了影响。
如果不想让L2受影响,可以在给L2赋值为L1引用的副本
L2 = L1[:]
最后再说一个python会缓存并复用小的整数和小的字符串。就像刚开始a=10,后面a又赋值为字符串,这个对象10并不会被立刻回收,它可能仍被保存在一个系统表中,等待下一次变量引用10来重复利用。尽管这样,大多数种类的对象都会在不被引用时马上回收。不过由于缓存机制会影响一些相等问题,如下
X = 10
Y = 10
X == Y #返回True
X is Y #如果没有缓存机制,那么会是False,但是返回的是True
都会在不被引用时马上回收。不过由于缓存机制会影响一些相等问题,如下
X = 10
Y = 10
X == Y #返回True
X is Y #如果没有缓存机制,那么会是False,但是返回的是True
这个仅仅适用于有缓存的小的整数和小的字符串,如果把10替换为2.3这种浮点数,则返回的是False.