来自像 c++ 这样的语言(就像我一样),这往往是许多人在第一次学习 Python 时难以掌握的主题。
底线是:当你使用del XXX 时,你永远不会*在使用del 时删除一个对象。您只是在删除一个对象引用。然而,在实践中,假设没有其他对 instance2 对象的引用,将其从列表中删除将根据需要释放内存。
如果您不了解对象和对象引用之间的区别,请继续阅读。
Python:按值传递,还是按引用传递?
您可能熟悉通过引用或值将参数传递给函数的概念。但是,Python 做的事情不同。参数总是由对象引用传递。阅读this article 以获得对这意味着什么的有用解释。
总结一下:这意味着当您将变量传递给函数时,您不会传递变量值的副本(按值传递),也不会传递对象本身 - 即,记忆中的价值。您正在传递间接引用内存中保存的值的名称对象。
这和del...有什么关系?
Well, I'll tell you.
假设你这样做:
def deleteit(thing):
del thing
mylist = [1,2,3]
deleteit(mylist)
...你认为会发生什么? mylist 是否已从全局命名空间中删除?
答案是否定的:
assert mylist # No error
原因是当您在deleteit 函数中执行del thing 时,您只是删除了对对象的本地对象引用。该对象引用仅存在于函数内部。作为侧边栏,您可能会问:是否可以在函数内部从全局命名空间中删除对象引用?答案是肯定的,但是你必须先声明对象引用是全局命名空间的一部分:
def deletemylist():
global mylist
del mylist
mylist = [1,2,3]
deletemylist()
assert mylist #ERROR, as expected
把它们放在一起
现在回到你原来的问题。当您在任何命名空间中执行以下操作时:
del XXX
...您还没有删除 XXX 表示的对象。你不能那样做。您只删除了 object reference XXX,它引用了内存中的某个对象。对象本身由Python memory manager 管理。这是一个非常重要的区别。
请注意,因此,当您覆盖某个对象的 __del__ 方法时,该方法会在 对象 被删除(而不是对象引用!)时调用:
class MyClass():
def __del__(self):
print(self, "deleted")
super().__del__()
m = MyClass()
del m
...__del__ 方法中的 print 语句不一定会在您执行 del m 之后立即出现。它只发生在对象本身被删除的时间点,这不取决于您。这取决于 Python 内存管理器。当所有命名空间中的所有对象引用都被删除后,__del__ 方法最终会被执行。但不一定立即。
当您删除作为列表一部分的对象引用时也是如此,就像在原始示例中一样。当您执行del a[1] 时,只会删除对a[1] 表示的对象的对象引用,并且该对象的__del__ 方法可能会或可能不会立即调用(尽管如前所述,它最终会被调用一次不再有对该对象的引用,并且该对象已被内存管理器进行垃圾回收)。
因此,不建议您将想要在del mything 上立即发生的事情放在__del__ 方法中,因为那样可能不会发生。
*我相信永远不会。不可避免地有人会否决我的回答并发表评论讨论规则的例外情况。但是什么。