Double-Lambda Python

Double-Lambda Python

Is it feasible to execute multiple statements in a lambda? The app_data dictionary in my controller has a property "listbox" that stores a selected listbox value. A button on SelectPage has a lambda command that changes frames to NextPage.

Can the frame switch and app_data set statements both occur in a double-lambda? If so, what might this look like? Or do these operations have to occur separately (i.e. in a button click event)?

Update #1:

Issue isolated to lambda expression since just putting a constant (i.e. 1) in like .set(1) does not save either.

Update #2: Using a lambda with doStuff(controller) solves the problem somewhat, but the listbox reference throws an error:

value = self.listbox.get(self.listbox.curselection()) AttributeError: 'SelectPage' object has no attribute 'listbox'

button:

   button1 = ttk.Button(self, text='Next Page',
                         command=lambda:self.doStuff(controller))

doStuff():

    def doStuff(self,controller):
        controller.show_frame(NextPage)
        controller.app_data["listbox"].set(1)
        value = self.listbox.get(self.listbox.curselection())
        print(value)
        print("do Stuff successful")

Full Code:

from tkinter import *
from tkinter import ttk



class MyApp(Tk):
    # Controller class
    def __init__(self):
        Tk.__init__(self)

        # App data in controller
        self.app_data = {"listbox":    StringVar(),
                         "entry":       StringVar(),
                         }

        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        self.frames = {}
        for F in (SelectPage, NextPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky = NSEW)
        self.show_frame(SelectPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def get_page(self,classname):
        for page in self.frames.values():
            if str(page.__class__.__name__) == classname:
                return page
        return None

class SelectPage(ttk.Frame):

    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        ttk.Label(self, text='Select Page').grid(padx=(20,20), pady=(20,20))

        listbox = Listbox(self,exportselection=0)
        listbox.grid()
        for item in [0,1,2,3,4,5]:
            listbox.insert(END, item)
            print (item)
        entry1 = ttk.Entry(self, textvariable=self.controller.app_data["entry"], width=8)
        entry1.grid()
        button1 = ttk.Button(self, text='Next Page',
                          command=lambda:self.doStuff(controller)) # something like this lambda concept
        button1.grid()

    def doStuff(self,controller):
        controller.show_frame(NextPage)
        controller.app_data["listbox"].set(1)
        value = self.listbox.get(self.listbox.curselection())
        print(value)
        print("do Stuff successful")

class NextPage(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        ttk.Label(self, text='Next Page').grid(padx=(20,20), pady=(20,20))
        button1 = ttk.Button(self, text='Select Page',
                             command=lambda: controller.show_frame(SelectPage))
        button1.grid()
        button2 = ttk.Button(self, text='press to print', command=self.print_it)
        button2.grid()

    def print_it(self):

        value = self.controller.app_data["listbox"].get()
        print ('The value stored in StartPage some_entry = ' + str(value))
        value = self.controller.app_data["entry"].get()
        print ('The value stored in StartPage some_entry = ' + str(value))
app = MyApp()
app.title('Multi-Page Test App')
app.mainloop()
9

2 Answers

Your original lambda didn't work because show_frame(...) returns None, which short-circuits the and so the remaining expression doesn't execute. Just change it to an or. Since the event caller doesn't care what you return, you could also create a two item tuple for the two subexpressions. There are other problems, such as self.listbox doesn't exist, which I fixed, but others remain.

As a pedantic aside, lambda only accepts an expression, which is a subset of a statement.

self.listbox = listbox
button1 = ttk.Button(self, text='Next Page',
                  command=lambda: controller.show_frame(NextPage) or self.controller
                     .app_data["listbox"]
                     .set(self.listbox.get(self.listbox.curselection()))) # something like this lam

Is it feasible to execute multiple statements in a lambda?

No. In general, you should avoid using lambda at all, and when you must use it you need to keep it as brief as possible. If you need to call more than a single function, create a new function that calls the other functions.

Also, if you're passing in an argument that is just an attribute of the object, you don't need lambda at all. I think the following code is much easier to read, much easier to understand, much easier to write, and thus much easier to maintain:

button1 = ttk.Button(self, text='Next Page',command=self.doStuff)
...
def doStuff(self):
    self.controller.show_frame(NextPage)
    self.controller.app_data["listbox"].set(1)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Marcus Vance
Author

Marcus Vance

Marcus Vance is a cybersecurity auditor and technology writer dedicated to educating the public about online safety, data privacy regulations, enterprise security, and emerging cyber threats.