Search code examples
pythonlambdapyqt5signals-slots

Python lambda pass-by-value mutable objects


After alot of research including on on stackoverflow i really can't understand why the code below does not work even after i set the Default value of my variable as alot of people recomended. here is the code

#initial nested array
outter = [["name1",1],["name2",2],["name3",3]]

for inner in outter:
    button = QPushButton()
    button.setText(inner[0])  // create a button with the text
    button.clicked.connect(lambda value=inner[1] : print(value))

The 3 buttons are created each with its respective name ( name1,name2,name3) but when i click them, it prints

False
False 
False

If i change this part of the code, all 3 buttons print the last value "3", as shown below,

 button.clicked.connect(lambda : print(inner[1]))
 3
 3
 3

I already tried doing all sorts crazy things to make this work, any advices?


Solution

  • I suppose you plainly forgot to press "save" after adding the value=… stuff.

    If I replay your problem

    outer = [["name1",1],["name2",2],["name3",3]]
    
    # this must have been your original approach and was the same as in the linked question
    ll = [lambda: inner[1] for inner in outer]
    print(ll[0](), ll[1](), ll[2]()) # gives 3 3 3
    
    # your approach
    ll = [lambda value=inner[1]: value for inner in outer]
    print(ll[0](), ll[1](), ll[2]()) # gives 1 2 3
    
    # alternative approach
    ll = [lambda value=inner: value[1] for inner in outer]
    print(ll[0](), ll[1](), ll[2]()) # gives 1 2 3
    

    As was mentioned in the previously linked question, the lambda always accesses the inner variable via its closure, only getting its last and persisting value.

    The alternative approaches go the way to link the lambda to the value which is current at creation time and should definitely work. (Note that instead of doung things with buttons, I concentrate on the labda problem.)

    Another way could be to have a function which creates "printers" as needed:

    def printer(value):
        return lambda: print(value)
    

    With them, you can easily create lambdas which do as you want: print the given value, such as

    button.clicked.connect(printer(inner[1]))print(value))