<div dir="ltr">As far as I can tell from experimentation (see code below), the refcounting behavior of connect() is:<br><br>* If the receiver is a bound method, a weak reference is held to the object and a bare pointer is held to the function. If the object is collected, the connection is automatically destroyed; if the function is collected (del Class.method), emit() segfaults.<br><br>* If the receiver is any other callable, a strong reference to it is held and leaked. It is never released even when the connection is destroyed.<br><br>That behavior seems to be consistent across PyQt 4, 5, and 6.<br><br>The behavior of PySide is:<br><br>* If the receiver is a bound method, PySide and PySide2 behave like PyQt (including the crash), but PySide6 holds a strong reference to the function and releases it when the connection is destroyed.<br><br>* If the receiver is any other callable, all PySide versions hold a strong reference to it and release it when the connection is destroyed.<br><br>I think that PyQt should do what PySide6 does.<br><br>Aside from crash avoidance always being good, holding a reference to the function of a bound method would allow for code like<br><br>    obj1.signal.connect((lambda obj2: ...).__get__(obj2))<br><br>to make a connection to what appears to Qt to be a method of obj2. There's a message on this list asking how to do that, which got no replies: <a href="https://www.riverbankcomputing.com/pipermail/pyqt/2020-November/043390.html">https://www.riverbankcomputing.com/pipermail/pyqt/2020-November/043390.html</a><br><br><br>There seems to be no description of connect()'s refcounting behavior in the documentation of any version of PyQt or PySide, except for an inaccurate paragraph in PyQt4's page on old-style SIGNAL and SLOT. The fact that bound methods are treated differently from all other callables is non-obvious and important, and I think it should be mentioned prominently.<br><br><br>Test code:<br><br>    #from PyQt4.QtCore import QObject, pyqtSignal as Signal<br>    #from PyQt5.QtCore import QObject, pyqtSignal as Signal<br>    from PyQt6.QtCore import QObject, pyqtSignal as Signal<br>    #from PySide.QtCore import QObject, Signal<br>    #from PySide2.QtCore import QObject, Signal<br>    #from PySide6.QtCore import QObject, Signal<br><br>    class Source(QObject):<br>        signal = Signal()<br><br>        def __del__(self):<br>            print('Source deleted')<br><br>    class Sink:<br>        def __call__(self):<br>            print('Sink called')<br><br>        def __del__(self):<br>            print('Sink deleted')<br><br>    source = Source()<br>    sink = Sink()<br><br>    if False:  # segfaults except on PySide6<br>        Sink.foo = lambda self: print('Ephemeral method called')<br>        source.signal.connect(sink.foo)<br>        del Sink.foo<br>        source.signal.emit()<br><br>    source.signal.connect(sink)<br>    del source<br>    del sink<br><br>In all PyQt versions this prints only "Source deleted". In all PySide versions it also prints "Sink deleted".<br><br>I tried making Sink inherit from QObject, making the Sink method a slot, explicitly destroying the connection with disconnect(), and forcing garbage collection, but it made no difference.<br><div><br></div></div>