Call destructor of an element from a list

2019-05-21 09:16发布

I have something like that:

a = [instance1, instance2, ...]

if I do a

del a[1]

instance2 is removed from list, but is instance2 desctructor method called?

I'm interested in this because my code uses a lot of memory and I need to free memory deleting instances from a list.

3条回答
在下西门庆
2楼-- · 2019-05-21 09:53

Coming from a language like c++ (as I did), this tends to be a subject many people find difficult to grasp when first learning Python.

The bottomline is this: when you do del XXX, you are never* deleting an object when you use del. You are only deleting an object reference. However, in practice, assuming there are no other references laying about to the instance2 object, deleting it from your list will free the memory as you desire.

If you don't understand the difference between an object and an object reference, read on.

Python: Pass by value, or pass by reference?

You are likely familiar with the concept of passing arguments to a function by reference, or by value. However, Python does things differently. Arguments are always passed by object reference. Read this article for a helpful explanation of what this means.

To summarize: this means that when you pass a variable to a function, you are not passing a copy of the value of the variable (pass by value), nor are you passing the object itself - i.e., the address of the value in memory. You are passing the name-object that indirectly refers to the value held in memory.

What does this have to do with del...?

Well, I'll tell you.

Say you do this:

def deleteit(thing):
    del thing

mylist = [1,2,3]
deleteit(mylist)

...what do you think will happen? Has mylist been deleted from the global namespace?

The answer is NO:

assert mylist # No error

The reason is that when you do del thing in the deleteit function, you are only deleting a local object reference to the object. That object reference exists ONLY inside of the function. As a sidebar, you might ask: is it possible to delete an object reference from the global namespace while inside a function? The answer is yes, but you have to declare the object reference to be part of the global namespace first:

def deletemylist():
    global mylist
    del mylist

mylist = [1,2,3]
deletemylist()
assert mylist #ERROR, as expected

Putting it all together

Now to get back to your original question. When, in ANY namespace, you do this:

del XXX

...you have NOT deleted the object signified by XXX. You CAN'T do that. You have only deleted the object reference XXX, which refers to some object in memory. The object itself is managed by the Python memory manager. This is a very important distinction.

Note that as a consequence, when you override the __del__ method of some object, which gets called when the object is deleted (NOT the object reference!):

class MyClass():
    def __del__(self):
        print(self, "deleted")
        super().__del__()

m = MyClass()
del m

...the print statement inside the __del__ method does not necessarily occur immediately after you do del m. It only occurs at the point in time the object itself is deleted, and that is not up to you. It is up to the Python memory manager. When all object references in all the namespaces have been deleted, the __del__ method will eventually be executed. But not necessarily immediately.

The same is true when you delete an object reference that is part of a list, like in the original example. When you do del a[1], only the object reference to the object signified by a[1] is deleted, and the __del__ method of that object may or may not be called immediately (though as stated before, it will eventually be called once there are no more references to the object, and the object is garbage collected by the memory manager).

As a result of this, it is not recommended that you put things in the __del__ method that you want to happen immediately upon del mything, because it may not happen that way.


*I believe it is never. Inevitably someone will likely downvote my answer and leave a comment discussing the exception to the rule. But whatevs.

查看更多
虎瘦雄心在
3楼-- · 2019-05-21 10:05

No. Calling del on a list element only removes a reference to an object from the list, it doesn't do anything (explicitly) to the object itself. However: If the reference in the list was the last one referring to the object, the object can now be destroyed and recycled. I think that the "normal" CPython implementation will immediately destroy and recycle the object, other variants' behaviour can vary.

查看更多
时光不老,我们不散
4楼-- · 2019-05-21 10:09

If your object is resource-heavy and you want to be sure that the resources are freed correctly, use the with() construct. It's very easy to leak resources when relying on destructors. See this SO post for more details.

查看更多
登录 后发表回答