Destructor being called multiple times - python

i am trying to check how new , init and del works and was running below mentioned code , what caught my attention is destructor is being called twice and i don't know the exact reason of it, Can anyone help with it?
class Example:
# creation of new instance
def __new__(cls):
print("example created", object.__new__(cls))
return object.__new__(cls)
# Initializing
def __init__(self):
print("Example Instance.")
# Calling destructor
def __del__(self):
print("Destructor called, Example deleted.")
obj = Example()
del obj

From geeksforgeeks:
The __del__() method is a known as a destructor method in Python. It is called when all references to the object have been deleted i.e when an object is garbage collected. Note : A reference to objects is also deleted when the object goes out of reference or when the program ends.
Python automatically manages memory for you using reference counting and garbage collector. That is, references unnecessary are being automatically deleted.
So you need to create it explicitly to avoid this behavior.
class Example:
instance = ""
# creation of new instance
def __new__(cls):
instance = object.__new__(cls)
print("example created", instance)
return instance
# Initializing
def __init__(self):
print("Example Instance.")
# Calling destructor
def __del__(self):
print("Destructor called, Example deleted.")
result:
>>> obj = Example()
example created <__main__.Example object at 0x7fd0b3efa5b0>
Example Instance.
>>> del obj
Destructor called, Example deleted.
>>>

Related

Weak reference callback is not called

The callback assigned to a weak reference object is not called when the strong reference is destroyed, the .callback attribute confirms the function was assigned. Why is it not being called?
It seems the weak reference to the function Foo.bar is not destroyed when the strong reference is destroyed, why?
def is_dead(wr):
print("Callback function called!")
class Foo(object):
def bar(self): pass
method = Foo.bar
wr = weakref.ref(method, is_dead)
method = None
print(wr.__callback__)

Weakref to an instance does not work after deletion, but works for the whole class when deleted

Say we have a class Test which has a static method as so:
import weakref
class Test:
#staticmethod
def hello():
return 'Hello'
We create an instance of Test - call its method, we also create a weak reference to the instance as well. Then delete the variable and see if we can call the method again:
test_ins = Test()
test_ins_weakref = weakref.ref(test_ins)
print(test_ins.hello())
print("---deleting test_ins---")
del test_ins
print(test_ins_weakref().hello())
Before we delete the instance, it works (as it should) - but then it doesn't (Checking the type yields NoneType and it of course does not have a method called hello).
If we don't perform the del operation on the instance, we would get <class '__main__.Test'> and thus we can call the method.
Now lets try deleting the class itself:
test_ins = Test()
test_ins_weakref = weakref.ref(test_ins)
print(test_ins.hello())
print("---deleting test_ins---")
del Test
print(test_ins_weakref().hello())
It works.
My main question is if someone can explain what is happening here, because when I read the code I don't understand how and why it works that way.
Deleting a whole class doesn't mean that now all its instances point to nothing? Because, after deleting the class Test we cannot access the method nor create new instances..
new_ins = Test()
print(Test().hello())
Both yield
NameError: name 'Test' is not defined
However we can tweak things "around" and use the working instance type to create a new one:
new_ins = type(test_ins)()
print(new_ins.hello())
Works just fine.
From the docs:
when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else.
That's why in this block:
test_ins = Test()
test_ins_weakref = weakref.ref(test_ins)
print(test_ins.hello())
print("---deleting test_ins---")
del test_ins
print(test_ins_weakref().hello())
the last line produces an error (the object to which test_ins_weakref references was destroyed by garbage collector).
In this block:
test_ins = Test()
test_ins_weakref = weakref.ref(test_ins)
print(test_ins.hello())
print("---deleting test_ins---")
del Test
print(test_ins_weakref().hello())
You delete Test but the class of the test_ins is still in the memory. It wasn't collected because it has a strong reference to it.
It can be seen here:
import weakref
class Test:
#staticmethod
def hello():
return 'Hello'
test_ins = Test()
print(f"{id(Test)=}")
del Test
print(f"{id(test_ins.__class__)=}")
It will give the same id because the objects is still alive.
So when you do del Test you are not deleting the object from the memory, you are removing the variable Test that was referencing the object.
If there is at least one strong reference to that object, it won't be deleted from the memory.
That's why this block:
new_ins = type(test_ins)()
print(new_ins.hello())
works.

Destructor in Python

Below is the code. I did not manually del object reference but then also in VScode __del__ gets invoked. But the same piece of code is working in Jupyter Notebook. This means, __del__ is not called automatically. Is there any problem with the code? Confused. Does VScode handle Garbage Collector (automatically)? Googled a lot, but did not get an appropriate answer.
class Student:
# constructor
def __init__(self, name):
print('Inside Constructor')
self.name = name
print('Object initialized')
def show(self):
print('Hello, my name is', self.name)
# destructor
def __del__(self):
print('Inside destructor')
print('Object destroyed')
# create object
s1 = Student('Emma')
s2 = s1;
s3 = s1;
print("Id of S1: " , id(s1));
It's better to call __del__ a finalizer method because this method gets called exactly when the object is about to be garbage collected. (like when all references to that object are gone.)
In VSCode or even when you run your script in terminal, when the interpreter reaches the end of the file it will terminate the execution and the process, therefore any objects will be destroyed. So you can see that this method gets called. So even if you didn't del the references to the object explicitly, all the references will be gone at the end.
Jupyter notebook on the other hand is still running, and also maybe it keeps a reference to your object for internal works...
Put a simple input() in the last line of your code, you can see that the __del__ wont get called in VScode or terminal as well.

what does 'self.variable = self' means in python

As far I know in python 'self' represents the object of a class. Recently I found a code where in the constructor(__init__) a variable value is assigned to 'self' like below:
self.x = self
Can anyone please explain what kind of value is actually assigned to x?
It creates a circular reference. self is bound to the instance on which the method is called, so setting self.x = self just creates a reference to the instance on the instance.
This is a generally silly thing to do, and potentially harmful to the memory performance of your program. If the class also defines the object.__del__() method then this will prevent the object from being garbage collected, causing a memory leak in all CPython releases < 3.4 (which implements PEP 442):
>>> import gc
>>> class SelfReference(object):
... def __init__(self):
... self.x = self
... def __del__(self):
... pass
...
>>> s = SelfReference()
>>> s.x is s # the instance references itself
True
>>> del s # deleting the only reference should clear it from memory
>>> gc.collect()
25
>>> gc.garbage # yet that instance is *still here*
[<__main__.SelfReference object at 0x102d0b890>]
The gc.garbage list contains everything the garbage collector cannot clean up due to circular references and __del__ methods.
I suspect that you found one of the very few actual usecases for assigning self to a an attribute anyway, which is the usecase davidb mentions: setting self.__dict__ to self if self is a mapping object, to 'merge' attribute and subscription access into one namespace.
Even if this kind of assignments can generally seem not a good idea, yet there are cases where it is indeed useful and elegant.
Here is one of those cases:
class Dict(dict):
'''Dictionary subclass allowing to access an item using its key as an
attribute.
'''
def __init__(self, *args, **kwargs):
super(Dict, self).__init__(*args, **kwargs)
self.__dict__ = self
Here is a simple usage example:
>>> d = Dict({'one':1, 'two':2})
>>> d['one']
1
>>> d.one
1

Delete an object and all references to it in Python?

Is there a way to remove all references to an object at once? I know that's unpythonic, so I'll explain what I'm trying to do and maybe someone knows a better way.
I'm writing an object-oriented wrapper around a SWIG wrapper for a C library. When a proxy for one of the C objects is deleted, it also deletes child objects (directly in C). I'd like that to also trigger deletion of their proxy objects in Python. Otherwise I run into a situation where there are Python objects carrying around invalid pointers which will segfault if they're accessed.
It looks sort of like this:
class Parent(object):
def __init__(self):
self.ptr = swig.createParent()
def __del__(self):
swig.deleteParent(self.ptr) # also deletes children
class Child(object):
def __init__(self, parent):
self.ptr = swig.createChild(parent)
def __del__(self):
swig.deleteChild(self.ptr)
And this is the situation I'm worried about:
p = Parent()
c = Child(parent)
del p
# accessing c.ptr now would be bad right?
If I understand you correctly, you are wrapping some C code, and the C code has a destructor that can be called. After that, any attempt to use the pointer to the C code object causes a fatal crash.
I am not sure of your exact situation, so I am going to give you two alternate answers.
0) If the C object can be freed for some reason out of your control, and you need to make sure your Python wrapper code doesn't crash, you need to make the Python wrapper know whether the C object is available or not. Make your Python object handle the pointer no longer being valid. You could raise a Python exception, return an error code, or just have the method functions become no-op functions, depending on what you are doing. The C object going away doesn't free the Python object, so you can handle this cleanly.
1) If the C object is only freed when the Python object is freed, you don't have a problem. Python references, when they go out of scope or you call del() on them, do not free the Python object; they just decrement the reference count on that object. When the reference count goes to zero, then the object is freed and your __del__() method function can call into the C code to free the C object.
You can watch how it works by running this code:
class DelTest(object):
def __init__(self):
print "__init__() called: object %08x created" % id(self)
def __del__(self):
print "__del__() called: object %08x destroyed" % id(self)
print "begin"
print "creating object, binding to name d"
d = DelTest()
print "adding reference bound to name x"
x = d
print "adding reference bound to lst[0]"
lst = []
lst.append(d)
print "deleting lst"
del(lst)
print "deleting x"
del(x)
print "deleting d"
del(d)
print "end"
Output from the above:
begin
creating object, binding to name d
__init__() called: object 01e4db50 created
adding reference bound to name x
adding reference bound to lst[0]
deleting lst
deleting x
deleting d
__del__() called: object 01e4db50 destroyed
end
A note about the behavior of __del__() method.
del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x‘s reference count reaches zero.
Therefore even if you delete parent, it does not necessarily mean that __del__ is executed immediately until there are any references to it. Here is an example.
>>> class C(object):
... def __del__(self):
... print "deleting object of type: %s" %self.__class__
...
>>> class D(object):
... def __init__(self, parent):
... self.parent = parent
... def __del__(self):
... print "deleting object of type: %s" % self.__class__
...
>>> c = C()
>>> d = D(c)
>>> del c
>>> del d
deleting object of type: <class '__main__.D'>
deleting object of type: <class '__main__.C'>
Note that the __del__ method of C is called after the del d call.

Categories