在Python中,对象是通过存储指向该对象的内存地址来创建的。例如:
class SomeObject():
def __init__(self, something, something_else):
self.something = something
self.something_else = something_else
def some_method(self):
return str(self.something)
a_variable_holding_a_memory_address = SomeObject(something=94, something_else=243)
a = a_variable_holding_a_memory_address
print(id(a) == id(a_variable_holding_a_memory_address))
输出:
True
如果你不知道,
id()
会返回Python变量的内存地址。
因此,如果你删除
a_variable_holding_a_memory_address
:
class SomeObject():
def __init__(self, something, something_else):
self.something = something
self.something_else = something_else
def some_method(self):
return str(self.something)
a_variable_holding_a_memory_address = SomeObject(something=94, something_else=243)
a = a_variable_holding_a_memory_address
print(a_variable_holding_a_memory_address)
del a_variable_holding_a_memory_address
print(a)
输出:
<__main__.SomeObject object at 0x7f80787ea940>
<__main__.SomeObject object at 0x7f80787ea940>
发生的只是一个变量保存了一个对象(或引用)的内存地址被删除了,但是地址本身并没有被删除,因为还存在另一个对它的引用。
现在看看这个:
import ctypes
class SomeObject():
def __init__(self, something, something_else):
self.something = something
self.something_else = something_else
def some_method(self):
return str(self.something)
a_variable_holding_a_memory_address = SomeObject(something=94, something_else=243)
a = a_variable_holding_a_memory_address
object_id = id(a_variable_holding_a_memory_address)
print(ctypes.cast(object_id, ctypes.py_object).value)
del a_variable_holding_a_memory_address
print(ctypes.cast(object_id, ctypes.py_object).value)
del a
print(ctypes.cast(object_id, ctypes.py_object).value)
输出:
<__main__.SomeObject object at 0x7fbd04b5d3a0>
<__main__.SomeObject object at 0x7fbd04b5d3a0>
<__main__.SomeObject object at 0x7fbd04b5d3a0>
zsh: segmentation fault python file.py
这个分段错误是因为程序尝试引用一个空内存地址。
使用Python语言相对于C语言的优点之一是它可以防止这种内存错误,除非你真的想让程序崩溃,就像在这个例子中一样。这包括自动垃圾回收(Python解释器的一部分,通过清空未使用的内存地址来释放内存)。
但是你看,当两个引用都存在时,内存地址保持一个对象。
当一个被删除时,该地址不会被删除,因为仍然存在一个引用该地址的引用。这就是自动垃圾收集器的工作原理。
当两者都被删除时,地址也被删除,因此出现了错误。还应注意的是,仅仅将变量赋予不同的值,如下所示:
a = "I now hold a str object"
改为:
del a
这也是可行的,因为对象的引用被替换为对另一个对象的引用,因此少了一个引用。
这不仅适用于用户定义的对象,还适用于Python中附带的非常基本的对象,通常被认为是理所当然的,例如str
、int
、list
或dict
等等。最终,在Python中变量所持有的就是一个对象。除非它持有像True
、False
或None
这样的布尔值。但是这些也是对象。Python文档中关于None
的说明如下:
一个经常用来表示缺少值的对象,例如当默认参数没有传递给函数时。对None的赋值是非法的并会引发SyntaxError。None是NoneType类型的唯一实例。
引用计数:
在Python中,对象的引用计数是持有对该对象引用的变量数量。当该计数达到零时,将调用对象的__del__
方法并销毁它(除非__del__
按照文档中概述的执行以下操作:
虽然不建议这样做,但是del()方法可以通过创建对实例的新引用来推迟实例的销毁。这被称为对象复活。当一个复活的对象即将被销毁时,是否会再次调用del()取决于具体实现;当前的CPython实现只会调用一次。
如果您想要一个不会保持对象存活的对象引用,则有一种称为weakref
的引用类型:
import weakref
class SomeObject():
def __init__(self, something, something_else):
self.something = something
self.something_else = something_else
def some_method(self):
return str(self.something)
reference = SomeObject('something', 'something_else')
print(reference)
weak_reference = weakref.ref(reference)
print(weak_reference)
del reference
print(weak_reference)
输出:
<__main__.SomeObject object at 0x7f305b017880>
<weakref at 0x7f305ae7c630; to 'SomeObject' at 0x7f305b017880>
<weakref at 0x7f305ae7c630; dead>
weakref
,weakreference
变成了一个指向空的对象,即一个死亡对象。
回到垃圾回收,通过使用内置库 gc
,您可以更改 Python 垃圾回收器释放内存的一些方式。
其中之一是禁用垃圾回收:
import gc
import ctypes
gc.disable()
class SomeObject():
def __init__(self, something, something_else):
self.something = something
self.something_else = something_else
def some_method(self):
return str(self.something)
a_variable_holding_a_memory_address = SomeObject(something=94, something_else=243)
a = a_variable_holding_a_memory_address
object_id = id(a_variable_holding_a_memory_address)
print(ctypes.cast(object_id, ctypes.py_object).value)
del a_variable_holding_a_memory_address
print(ctypes.cast(object_id, ctypes.py_object).value)
del a
print(ctypes.cast(object_id, ctypes.py_object).value)
输出:
<__main__.SomeObject object at 0x7f305ae6df10>
<__main__.SomeObject object at 0x7f305ae6df10>
<_ast.Interactive object at 0x7f305ae6df10>
即使没有对它的引用,该对象仍然存在。通常情况下,自动垃圾回收器会释放用于存储对象的内存空间,但我使用gc.disable()
禁用了它。
_ast.Interactive
对象是什么?
首先,这里是Python如何由其编写的C代码解释的简单概述:
- 将代码解析为标记数组
- 使用标记数组创建抽象语法树(
ast
,即_ast
)。
- 从抽象语法树生成字节码
- 运行字节码
因此,在删除所有对对象的引用并禁用自动垃圾回收后,剩下的只是比字节码高一级的对象代码。
pygame
或类似的东西?该库可能提供了一种从显示中删除对象的方法。将这些变量赋值为None
可能会或可能不会起作用,因为你只是在删除一个引用,但该库可能有其他对这些对象的引用以使其保持活动状态。 - Bakuriubarbox1
来实现什么目标? - Ethan Furmanbarbox1 = None
,由于barbox1
是该对象唯一的引用,你已经删除了该对象并释放了该对象占用的内存,不需要担心变量名本身。 - Zhang Buzz