Is it possible to delete an object form inside its class?
class A():
def __init__(self):
print("init")
self.b="c"
def __enter__(self):
print("enter")
return self
def __exit__(self, type, value, traceback):
print("exit")
with A() as a:
print(a.b)
print(a.b)
returns:
init
enter
c
exit
c
How comes I still have access to the a object after exiting the with ? Is there a way to auto-delete the object in __exit__?
Yes and no. Use del a after the with clause. This will remove the variable a who is the last reference holder on the object.
The object itself (i. e. in __exit__()) cannot make the ones who know about it and hold a reference (i. e. the code at the with clause) forget this. As long as the reference exists, the object will exist.
Of course, your object can empty itself in the __exit__() and remain as a hollow thing (e. g. by del self.b in this case).
Short answer: it is (to some extent) possible, but not advisable at all.
The with part in Python has no dedicated scope, so that means that variables defined in the with statement are not removed. This is frequently wanted behavior. For example if you load a file, you can write it like:
with open('foo.txt') as f:
data = list(f)
print(data)
You do not want to remove the data variable: the with is here used to ensure that the file handler is properly closed (and the handler is also closed if an exception occurs in the body of the with).
Strictly speaking you can delete local variables that refer to the A() object, by a "hackish" solution: we inspect the call stack, and remove references to self (or another object), like:
import inspect
class A(object):
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
locs = inspect.stack()[1][0].f_locals
ks = [k for k, v in locs.items() if v is self]
for k in ks:
del locs[k]
Then it will delete it like:
>>> with A() as a:
... pass
...
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
But I would strongly advice against that. First of all, if the variabe is global, or located outside the local scope, it will not get removed here (we can fix this, but it will introduce a lot of extra logic).
Furthermore it is not said that the variable even exists, if the variable is iterable, one can define it like:
# If A.__enter__ returns an iterable with two elements
with A() as (foo, bar):
pass
So then these elements will not get recycled. Finally if the __enter__ returns self, it is possible that it "removes too much", since one could write with foo as bar, and then both foo and bar will be removed.
Most IDEs will probably not be able to understand the logic in the __exit__, anyway, and hence still will include a in the autocompletion.
In general, it is better to simply mark the object as closed, like:
import inspect
class A(object):
def __init__(self):
self.closed = False
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.closed = True
def some_method(self):
if self.closed:
raise Exception('A object is closed')
# process request
The above is also the way it is handled for a file handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With