Skip to content Skip to sidebar Skip to footer

Memory_profiler While Using Lambda Expression To Connect Slots

I am experimenting memory_profiler in Python3 following https://medium.com/zendesk-engineering/hunting-for-memory-leaks-in-python-applications-6824d0518774 thanks to @eyllanesc he

Solution 1:

The problem pointed out in this post does not have to do with PyQt5 but with the lambda functions since, like any function, it creates a scope and stores memory, to test what the OP points out I have created the following example:

from PyQt5 import QtCore, QtWidgets


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.counter = 0
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.on_timeout)
        self.timer.start(1000)

        self.button = QtWidgets.QPushButton("Press me")
        self.setCentralWidget(self.button)

    @QtCore.pyqtSlot()
    def on_timeout(self):
        self.counter += 1
        if self.counter % 2 == 0:
            self.connect()
        else:
            self.disconnect()

    def connect(self):
        self.button.setText("Connected")
        self.button.clicked.connect(lambda checked: None)
        # or
        # self.button.clicked.connect(lambda checked, v=list(range(100000)): None)

    def disconnect(self):
        self.button.setText("Disconnected")
        self.button.disconnect()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
  • self.button.clicked.connect(lambda checked: None)

enter image description here

  • self.button.clicked.connect(lambda checked, v=list(range(100000)): None)

enter image description here

As observed at the time of connection and disconnection, the memory consumed increases since the lambda maintains the information of v=list(range(100000)).


But in your code the closure stores only the variable "j" which is minimum:

getattr(self, i).clicked.connect(lambda pippo, j=i: self.printo(j))

To see how this variable affects I am going to eliminate the unnecessary code for the test (hangman005.py, etc) in addition to offering alternatives:

  • without connect:
class MainMenu(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainMenu, self).__init__()
        uic.loadUi("main_window2.ui", self)
        self.hangman()

    def hangman(self):
        letters = "ABCDEFGHIJKLMNOPQRSTYVWXXYZ"
        for i in letters:
            pass

    def printo(self, i):
        print("oooooooooooooooooooooo :", i)

enter image description here

  • with lambda:
class MainMenu(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainMenu, self).__init__()
        uic.loadUi("main_window2.ui", self)
        self.hangman()

    def hangman(self):
        letters = "ABCDEFGHIJKLMNOPQRSTYVWXXYZ"
        for i in letters:
            getattr(self, i).clicked.connect(lambda pippo, j=i: self.printo(j))

    def printo(self, i):
        print("oooooooooooooooooooooo :", i)

enter image description here

  • with functools.partial
class MainMenu(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainMenu, self).__init__()
        uic.loadUi("main_window2.ui", self)
        self.hangman()

    def hangman(self):
        letters = "ABCDEFGHIJKLMNOPQRSTYVWXXYZ"
        for i in letters:
            getattr(self, i).clicked.connect(partial(self.printo, i))

    def printo(self, i):
        print("oooooooooooooooooooooo :", i)

enter image description here

  • once connect
class MainMenu(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainMenu, self).__init__()
        uic.loadUi("main_window2.ui", self)
        self.hangman()

    def hangman(self):
        letters = "ABCDEFGHIJKLMNOPQRSTYVWXXYZ"
        for i in letters:
            getattr(self, i).setProperty("i", i)
            getattr(self, i).clicked.connect(self.printo)

    def printo(self):
        i = self.sender().property("i")
        print("oooooooooooooooooooooo :", i)

enter image description here

As we can see there is no significant difference between all the methods so in your case there is no memory leak or rather it is very small.

In conclusion: Every time you create a lambda method it has a closure (j = i in your case) so the OP recommends taking that into account, for example in your case len(letters) * size_of(i) are consumed which at Being small letters and i makes it negligible, but if you otherwise store a heavier object unnecessarily then it will cause problems


Post a Comment for "Memory_profiler While Using Lambda Expression To Connect Slots"