Object deletes reference to self - garbage-collection

Does Python interpreter gracefully handles cases where an object instance deletes the last reference to itself?
Consider the following (admittedly useless) module:
all_instances = []
class A(object):
def __init__(self):
global all_instances
all_instances.append(self)
def delete_me(self):
global all_instances
self.context = "I'm still here"
all_instances.remove(self)
print self.context
and now the usage:
import the_module
a = the_module.A()
the_deletion_func = a.delete_me
del a
the_deletion_func()
This would still print I'm still here, but is there a race condition with Python's garbage collector which is about to collect the object instance?Does the reference to the object's function save the day?Does the interpreter keep references to the object whose code it is currently executing until it finishes?

No, there isn't any such race condition. You are clearing the reference, so the ref count drops to 1 and the object will be cleaned up once you delete the method reference.
The the_deletion_func reference points to a method, which points to the instance (as well as the class), so there is still a reference there.
Currently executing methods have a local variable self, which is a reference to the instance as well, but mostly it's the method wrapper that provides that reference.

Related

finding the caller object given its name only

I want to find the caller callable from within the called object, without explcitely forwarding the caller to the called as an object.
My current code looks something like this:
class Boo:
#classmethod
def foo(cls, aa, b2=2):
_ret = aa + b2
autolog(fn=Boo.foo, values={"input": locals(), "output": _ret}, message="This is what it should look like")
autolog_nameless(values={"input": locals(), "output": _ret}, message="This would be convenient")
return _ret
and yields
DEBUG | Boo.foo with aa=3.14159, b2=2 yields 5.14159. Message: This is what it should look like
DEBUG | cls=<class '__main__.Boo'>, aa=3.14159, b2=2, _ret=5.14159 yields 5.14159. Message: This would be convenient
The method autolog gets the locals() and the caller method fn, and parses them using the signature of the caller. This works nice and provides the desired output, but requires passing the caller as an object - something I'd like to avoid as I'm refractoring to include this feature and have about 1000 places to modify.
What I'd like to achieve is: pass locals() only; get the name of the caller within autolog_nameless, using inspect.stack()[1][3] or rather inspect.currentframe().f_back.f_code.co_name (latter has much less overhead), and using this - an possibly the information in locals() - find the caller object to inspect it for its signature.
The method autolog_nameless gets cls, actually the class as part of locals() (or would get self if the caller was a simple method), but I can't really do anything with it.
I'd think all the information required is given, but I just can't find a solution. Any help is greatly appreciated.
As it turns out it's quite simple: listing the methods of the class object found in locals() and searching by name should do the trick.
Code, without error checking:
# getting all methods of the class
methods = inspect.getmembers(locals()['cls'], predicate=inspect.ismethod)
# finding the callers name; won't work within the list comprehension for scope issues
_name = inspect.currentframe().f_back.f_code.co_name
# methods is a list of tuples, each tuple holds the name and the method object
fn = [x for x in methods if x[0] == _name][0][1]
and fn is the caller object to check the signature.
Note, locals()['cls'] works here as in the example we have a classmethod, but this is just the object that the called method belongs to.

QCheckbox issue [duplicate]

I am struggling to get this working.
I tried to transpose from a c++ post into python with no joy:
QMessageBox with a "Do not show this again" checkbox
my rough code goes like:
from PyQt5 import QtWidgets as qtw
...
mb = qtw.QMessageBox
cb = qtw.QCheckBox
# following 3 lines to get over runtime errors
# trying to pass the types it was asking for
# and surely messing up
mb.setCheckBox(mb(), cb())
cb.setText(cb(), "Don't show this message again")
cb.show(cb())
ret = mb.question(self,
'Close application',
'Do you really want to quit?',
mb.Yes | mb.No )
if ret == mb.No:
return
self.close()
the above executes with no errors but the checkbox ain't showing (the message box does).
consider that I am genetically stupid... and slow, very slow.
so please go easy on my learning curve
When trying to "port" code, it's important to know the basis of the source language and have a deeper knowledge of the target.
For instance, taking the first lines of your code and the referenced question:
QCheckBox *cb = new QCheckBox("Okay I understand");
The line above in C++ means that a new object (cb) of type QCheckBox is being created, and it's assigned the result of QCheckBox(...), which returns an instance of that class. To clarify how objects are declared, here's how a simple integer variable is created:
int mynumber = 10
This is because C++, like many languages, requires the object type for its declaration.
In Python, which is a dynamic typing language, this is not required (but it is possible since Python 3.6), but you still need to create the instance, and this is achieved by using the parentheses on the class (which results in calling it and causes both calling __new__ and then __init__). The first two lines of your code then should be:
mb = qtw.QMessageBox()
cb = qtw.QCheckBox()
Then, the problem is that you're calling the other methods with new instances of the above classes everytime.
An instance method (such as setCheckBox) is implicitly called with the instance as first argument, commonly known as self.
checkboxInstance = QCheckBox()
checkboxInstance.setText('My checkbox')
# is actually the result of:
QCheckBox.setText(checkboxInstance, 'My checkbox')
The last line means, more or less: call the setText function of the class QCheckBox, using the instance and the text as its arguments.
In fact, if QCheckBox was an actual python class, setText() would look like this:
class QCheckBox:
def setText(self, text):
self.text = text
When you did cb = qtw.QCheckBox you only created another reference to the class, and everytime you do cb() you create a new instance; the same happens for mb, since you created another reference to the message box class.
The following line:
mb.setCheckBox(mb(), cb())
is the same as:
QMessageBox.setCheckBox(QMessageBox(), QCheckBox())
Since you're creating new instances every time, the result is absolutely nothing: there's no reference to the new instances, and they will get immediately discarded ("garbage collected", aka, deleted) after that line is processed.
This is how the above should actually be done:
mb = qtw.QMessageBox()
cb = qtw.QCheckBox()
mb.setCheckBox(cb)
cb.setText("Don't show this message again")
Now, there's a fundamental flaw in your code: question() is a static method (actually, for Python, it's more of a class method). Static and class methods are functions that don't act on an instance, but only on/for a class. Static methods of QMessageBox like question or warning create a new instance of QMessageBox using the provided arguments, so everything you've done before on the instance you created is completely ignored.
These methods are convenience functions that allow simple creation of message boxes without the need to write too much code. Since those methods only allow customization based on their arguments (which don't include adding a check box), you obviously cannot use them, and you must code what they do "under the hood" explicitly.
Here is how the final code should look:
# create the dialog with a parent, which will make it *modal*
mb = qtw.QMessageBox(self)
mb.setWindowTitle('Close application')
mb.setText('Do you really want to quit?')
# you can set the text on a checkbox directly from its constructor
cb = qtw.QCheckBox("Don't show this message again")
mb.setCheckBox(cb)
mb.setStandardButtons(mb.Yes | mb.No)
ret = mb.exec_()
# call some function that stores the checkbox state
self.storeCloseWarning(cb.isChecked())
if ret == mb.No:
return
self.close()

Why changing object itself through class object doesn't work?

class Cat:
def func(self):
self = None
a = Cat()
print(a)
a.func()
print(a)
I thought I would get None with second print function, but I got same object addresses for both prints. Why can't I modify object with class method?
In the class method, self is an argument that becomes part of the local scope. Assigning to any local scope variable only changes the local scope. If you were to assign to an attribute of self such as
self.foo = “Bar”
Then you would modify the object itself.
Furthermore, the object is referenced by a in the calling (global) scope and that reference would prevent destruction of the object.
Put another way, self and a both refer to the same object and assigning self=None only removes one of those references.

Why is the deconstructor not automatically being called?

I'm working on an assignment for school and having some difficulty understanding the __del__ method. I understand that it is called after all the references to the object are deleted, but I'm not exactly sure how to get to that point. It states that the __del__ method should be called automatically, but I'm having a rough time even getting the del() to automatically call __del__ as I understand it should.
I've tried to manually call the del method and have tried looking at various sample coding. Something is just not clicking with me for this. The only way I can some-what get it to be called is by using this piece of code at the end:
for faq in faqs:
Faq.__del__(faq)
But I know that is not correct.
class Faq:
def __init__(self, question, answer):
self.question = question
self.answer = answer
return
def print_faq(self):
print('\nQuestion: {}'.format(self.question))
print('Answer: {}'.format(self.answer))
def __del__(self):
print('\nQuestion: {}'.format(self.question))
print('FAQ deleted')
faqs = []
faq1 = Faq('Does this work?', 'Yes.')
faqs.append(faq1)
faq2 = Faq('What about now?', 'Still yes.')
faqs.append(faq2)
faq3 = Faq('Should I give up?', 'Nope!')
faqs.append(faq3)
print("FAQ's:")
print('='*30)
for faq in faqs:
obj = Faq.print_faq(faq)
print()
print('='*30)
I expect the code to output the __del__ print statements to verify the code ran.
The method __del__ is called
when the instance is about to be destroyed
This happens when there are no more references to it.
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.
So the reason you don't see the expected prints is because each Faq object has 2 references to it:
The variable it is assigned to (faq1, faq2 ...)
A reference from the list faqs
So doing del faq1 is not enough as this will only leave one last reference from the list. To delete those references too, you can do del faqs[:].
As to the code posted here, I am guessing you expect to see the prints because when the program finishes all resources are released. Well that is true, but:
It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.
You bind faqN and faqs and you keep these references. You need to destroy bindings.
For example:
faq1 = None # del faq1
faq2 = None
faq3 = None
faqs = None # [] or del faqs

Python3: Why is __del__ called after reassignment of a new object variable?

Suppose I have the following code in Python:
class Test:
def __del__(self):
print("del is called")
a = Test()
a = Test()
yields to the following output:
`del is called`
Why is that and what is the concept behind this?
Is "del" called after every reassignment?
__del__ is called because there's no more references to the original a, causing it to be garbage-collected. See the Python docs for details.
You only get the output once, because
It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

Resources