函数中的 __del__() 方法: 销毁对象
Python通过调用__init__()方法构造当前类的实例化对象,而__del__()方法,是用来销毁实例化对象。
事实上在编写程序时,如果之前我们创建的类实例化对象后续不再使用,最好再合适位置手动将其销毁,释放其占用的内存空间(整个过程称为垃圾回收(简称:GC))
大多数情况下,Python开发者不需要手动进行垃圾回收,因为Python有自动的垃圾回收机制,能将不需要使用的实例对象进行销毁
(pycharm环境,python3.7.6)
实例一
# -*- coding:utf8 -*-classUser:
def__init__(self):
print("=== 调用 __init__() 方法构造对象 ===")
def__del__(self):
print("调用__del__() 销毁对象,(对象占用的内存被回收)")
u1=User()
print('#'*50)
运行结果:
D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/私有属性2.py
=== 调用 __init__() 方法构造对象 ===
##################################################
调用__del__() 销毁对象,(对象占用的内存被回收)
Process finished with exit code 0
注释:上面代码在交互式python下运行是不会调用__del__()方法的,也就不会打印出调用__del__() 销毁对象,(对象占用的内存被回收)
结论:Python 会在程序即将结束前调用__del__方法,自动帮我们销毁对象,进而释放对象占用的内存,注意__del__()方法调用的位置,在print('#'*50)语句之后。
实例二
# -*- coding:utf8 -*-classUser:
def__init__(self):
print("=== 调用 __init__() 方法构造对象 ===")
def__del__(self):
print("调用__del__() 销毁对象,(对象占用的内存被回收)")
u1=User()
delu1print('#'*50)
运行结果:
D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/私有属性2.py
=== 调用 __init__() 方法构造对象 ===
调用__del__() 销毁对象,(对象占用的内存被回收)
##################################################
Process finished with exit code 0
结论:手动删除对象,进而调用 __del__() 方法销毁内存对象,释放内存空间
实例三
# -*- coding:utf8 -*-classUser:
def__init__(self):
print("=== 调用 __init__() 方法构造对象 ===")
def__del__(self):
print("调用__del__() 销毁对象,(对象占用的内存被回收)")
u1=User()
# 添加一个引用u1对象的实例对象u2=u1delu1#print('#'*50)
运行结果:
D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/私有属性2.py
=== 调用 __init__() 方法构造对象 ===
##################################################
调用__del__() 销毁对象,(对象占用的内存被回收)
Process finished with exit code 0
结论:从运行结果可以看出来,执行该语句 del u1 调用 __del__()方法,但是并没有立即执行,而是在程序即将运行结束的时候,程序自动调用了__del__()方法,销毁了内存对象,这里涉及Python的垃圾回收机制
Python采用自动引用计数(简称:ARC(auto-refrence-count))的方式实现垃圾回收机制。该方法的核心思想是:每一个Python对象都会配置一个计数器,初始Python实例对象的计数器值都为0,如果有变量引用该实例对象,其计数器的值也会加1,依次类推;反之,每当一个变量取消对该实例对象的引用,计数器会减1。如果一个Python对象的计数器值为0,则表明没有变量引用该Python对象,即证明程序不再需要它了,此时Python就会自动调用__del__()方法将其回收
以实例二为例:实际上构建u1实例对象的过程分为2步,先使用User()调用该类中的__init__()方法构造出一个该类的对象(我们将其称为U,计数器为0),并立即用 u1 这个变量作为所建实例对象的引用 (U 的计数器值 + 1)。在此基础上,又有一个 u2 变量引用 u1(其实相当于引用User(),此时U的计数器再 +1),这时如果调用 del u1 语句,只会导致 C 的计数器减 1(值变为1),因此C的计数器值不为0,因此U不会被销毁(不会执行 __del__() 方法)。
实例四 1.0
# -*- coding:utf8 -*-classUser:
def__init__(self):
print("=== 调用 __init__() 方法构造对象 ===")
def__del__(self):
print("调用__del__() 销毁对象,(对象占用的内存被回收)")
u1=User()
u2=u1# del u1 # 从运行结果可以看出来,执行该语句没有调用 __del__()方法,而是在程序即将运行结束的时候,程序自动调用了__del__()方法,销毁了对象# print('#'*50)# print('\n')# del u2# print("*"*50)
运行结果:
D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/私有属性2.py
===调用 __init__() 方法构造对象===调用__del__() 销毁对象,(对象占用的内存被回收)
Process finished withexitcode0
实例四 1.1:
# -*- coding:utf8 -*-classUser:
def__init__(self):
print("=== 调用 __init__() 方法构造对象 ===")
def__del__(self):
print("调用__del__() 销毁对象,(对象占用的内存被回收)")
u1=User()
u2=u1delu1# 从运行结果可以看出来,执行该语句没有调用 __del__()方法,而是在程序即将运行结束的时候,程序自动调用了__del__()方法,销毁了对象print('#'*50)
# print('\n')# del u2print("*"*50)
运行结果:
D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/私有属性2.py
=== 调用 __init__() 方法构造对象 ===
##################################################
**************************************************
调用__del__() 销毁对象,(对象占用的内存被回收)
Process finished with exit code 0
实例四 1.2:
# -*- coding:utf8 -*-classUser:
def__init__(self):
print("=== 调用 __init__() 方法构造对象 ===")
def__del__(self):
print("调用__del__() 销毁对象,(对象占用的内存被回收)")
u1=User()
u2=u1delu1# 从运行结果可以看出来,执行该语句没有调用 __del__()方法,而是在程序即将运行结束的时候,程序自动调用了__del__()方法,销毁了对象print('#'*50)
print('\n')
delu2print("*"*50)
运行结果:
D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/私有属性2.py
=== 调用 __init__() 方法构造对象 ===
##################################################
调用__del__() 销毁对象,(对象占用的内存被回收)
**************************************************
Process finished with exit code 0
通过以上三个实例对比,我们不难发现:实例对象被引用了n次,计数器就为n,执行一次del 对象的引用那么所引用的这个实例对象的计数器就会自动减1,当实例对象的计数器减为零时,就会调用并且执行类中的__del__()方法,计数器不为零,则不会执行实例对象中的__del__()方法,那么就会等程序即将结束之前,python解释器会自动执行__del__()方法,销毁对象。