高阶python(三) : 可变类型与不可变类型,引用计数(垃圾回收)

一、可变与不可变类型

可变数据类型有: 列表 、 集合 、 字典
不可变数据类型有: 整数 、 str 、 tuple(要对元组做限制,只能存放数字和字符串的不可变元素)

我们可以通过 id() 这个函数来查看对象的内存地址,如果两个对象的内存地址相同则代表是可变类型,如果不相同则代表不可变类型

a = 1
print(id(a)) # 140716959916880
a = 2
print(id(a)) # 140716959916912

str1 = 'ni'
print(id(str1)) # 2469068104848
str1= 'nihao'
print(id(str1)) # 2469068105016

a = [1,2]
b = (a,3,4)
print(b,id(b),type(b)) # ([1, 2], 3, 4) 2785141536664 <class 'tuple'>
a[0] = 'nihoa'
print(b,id(b),type(b)) # ([1, 2], 3, 4) 2785141536664 <class 'tuple'>
print('虽然id没变,但元组本来就是不可变的,里面的列表是可变的,我们改变了列表')

list1 = [1,2]
print(id(list1)) # 2469067645576
list1.append(3)
print(id(list1)) # 2469067645576

dict1 = {'a':1,'b':2}
print(id(dict1)) # 2469099557296
dict1['c'] = 3
print(id(dict1)) # 2469099557296

# 简单理解:不可变数据类型更改后地址发生改变,可变数据类型更改地址不发生改变

正因为这样,我们有时候会遇到一些坑

a = 1
b = a
print(id(a),id(b)) # 140716996027216 140716996027216
a = a + 1
print(id(a),id(b)) # 140716996027248 140716996027216
print(b)  # 1
'''
一开始申明了a,a指向了1这个对象,然后申明了b,b指向了a这个对象
这时候相当于a,b同时指向了不可变对象1,所以id相同
然后随后对a进行新的申明,a = a+1
这时候a便指向了另一个id,不再是原本的id,但b仍是原本的id没有随着变化
所以最后 b = 1
'''
+ 与 += 的区别

'''
+= 操作首先会调用 __iadd__ 方法,如果没有则会调用__add__方法
__iadd__(a+=b): 接受两个参数,他会改变第一个参数的值(需要对象是可变的,所以对于不可变对象没有__iadd__这个方法)
__add__(a+b): 接受两个参数,返回他们的和,两个参数的值均不变
'''
hasattr(int, '__iadd__')  #False # hasattr() 判断是否有这个方法
hasattr(list, '__iadd__') #True

# list 拥有 __iadd__ 这个方法
x = [1,2]
y = x
y += [3]
print(x,y) # [1, 2, 3] [1, 2, 3]

# 如果不调用 __iadd__方法 直接调用 __add__方法 则会
x = [1,2]
y = x
y = y + [3]
print(x,y) # [1, 2] [1, 2, 3]

# int 没有__iadd__方法 即使调用了(+= 实际上)调用 __add__方法(a = a+b)
a = 1
b = a
a += b # 相当于 a = a + b (因为不可变类型没有__iadd__这个方法)
print(a,b) # 2 1

总结:

不可变数据类型在第一次声明赋值声明的时候, 会在内存中开辟一块空间(id值), 用来存放这个变量被赋的值

而这个变量实际上存储的, 并不是被赋予的这个值, 而是存放这个值所在空间的内存地址(id值)

通过这个地址,变量就可以在内存中取出数据了. 所谓不可变就是说, 我们不能改变这个数据在内存中的值(id值)

所以当我们改变这个变量的赋值时, 只是在内存中重新开辟了一块空间(新的id值)将这一条新的数据存放在这一个新的内存地址里

原来的那个变量就不在引用原数据的内存地址而转为引用新数据的内存地址了.

二、引用计数

Python内部使用了引用计数来跟踪内存中的对象

引用计数增加的情况
对象被创建:x = 1
创建对象后又被其他对象引用 : y = x
对象被作为参数传递给函数:count(x)
对象成为容器对象的一个元素:list1 = [x,1,2]
引用计数减少的情况
对象的别名被显式销毁:del y
对象的一个别名被赋值给其他对象:x = 123
对象被从一个窗口对象中移除:list1.remove(x)
窗口对象本身被销毁:del list1

当引用计数被减到0的时候则会被回收

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值