Python 对象引用、垃圾回收的详解

一、对象引用:

Python 和 Java中的变量本质不一样,Java的变量可以理解为一个盒子,用来容纳我们的对象,使用前需要声明,好分配给我们合适的内存空间。Python的变量可以理解为一个便利贴,先构造处对象,再将变量贴在对象上。

Python变量没有类型,对象才有,本质上是指针,同一个变量可以表示不同对象

举个栗子:

a = 6

实际分为三个步骤:

1.创建一个对象来代表值为6

2.创建一个变量a

3.将变量和新的对象6进行关联

Python中从变量到对象的连接叫引用,使用术语来说:

  • 变量是一个系统表的元素,拥有指向对象的连接空间
  • 对象是分配的一块内存,有足够的空间去表示它们所代表的值
  • 引用是自动形成的从变量到对象的指针

举个栗子,将a和b都贴在同一个对象上:

a = [1, 2, 3]
b = a  # 将a贴在b上
a.append(6)
print(a)  # 输出结果:[1, 2, 3, 6]
print(b)  # 输出结果:[1, 2, 3, 6]

print(a is b)  # 输出结果:True
print(id(a), id(b))  # 输出结果:2273456626568 2273456626568

1:is和==的区别:

is是判断两个变量引用对象id是否相等(内存地址)

==用于判断引用变量的值是否相等

整数

a = 123
b = 123
print(a == b)  # 输出结果:True
print(a is b)  # 输出结果:True

字符串

a = 'abc'
b = 'abc' 
print(a == b)  # 输出结果:True
print(a is b)  # 输出结果:True

列表

a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)  # 输出结果: True
print(a is b)  # 输出结果:False

字典

a = {"name": "fe_cow"}
b = {"name": "fe_cow"}
print(a == b)  # 输出结果:True
print(a is b)  # 输出结果:False

集合

a = {'a', 'b'}
b = {'a', 'b'}
print(a == b)  # 输出结果:True
print(a is b)  # 输出结果:False

总结:只要对象的值一样,那么a == b的值一定为True,但是在可变类型中,它们所在的内存地址是不同的,所以为Fasle。

注意:在变量和单例值之间比较时,应该使用is,最常用is检查变量绑定的值是不是None

表示为空:

x is None

表示不为空:

x is not None

二、垃圾回收:

1.垃圾回收详解:

主要采用引用计数算法:在Python中每个对象的引用都会有统计,当引用计数归零时,对象就会立即销毁,所占用的内存将被释放。

举个栗子:

a = object()
b = a
del a
print(b)
print(a)

# 输出结果如下:
<object object at 0x000001E4CAD3F080>  # 可以看出b的对象已经打印了
Traceback (most recent call last):
  File "C:/Users/lh9/PycharmProjects/request/Python魔法函数.py", line 462, in <module>
    print(a)
NameError: name 'a' is not defined

class A:
    def __del__(self):
        pass
# 类中有一个del方法,一般在垃圾回收时被调用

当a,b都指向object()时,object()的计数为2,删除a时,计数为1,当计数为0时内存就会被释放。

del 不删除对象,而是删除对象的引用

2.弱引用:

某些情况下可能需要保存对象的引用,但不留存对象本身,此时可以借助弱引用实现。弱引用不会妨碍对象被当做垃圾回收。

弱引用是一种低层机制,是weakref模块中WeakValueDictionary、WeakKeyDictionary和WeakSet等有用的集合类,以及finalize函数的底层支持。

弱引用所指对象可以是set,用户自定义的类,list和dict的子类。不可以是int、tuple的实例及子类,也不可以是list实例或dict实例。

简单的说:弱引用不会增加对象的引用数量,不会妨碍所指对象被当作垃圾回收

举个栗子:

In [1]: import weakref 

In [2]: my_set = {0, 1, 2, 3}

In [3]: wref = weakref.ref(my_set)  # 创建弱引用对象

In [4]: wref
Out[4]: <weakref at 0000000004FA52C8; to 'set' at 00000000045D3E48>

In [5]: wref()  # 返回被引用的对象, 会绑定到my_set变量
Out[5]: {0, 1, 2, 3}

In [6]: my_set = {2, 3}  # 重新绑定my_set, 减少了{0,1,2,3}的引用计数

In [7]: wref()  #  但变量仍然指向它
Out[7]: {0, 1, 2, 3}

In [8]: my_set = {4, 5}

In [9]: wref()
Out[9]: {0, 1, 2, 3}

In [10]: wref() is None
Out[10]: False