Lambda returning tuple question, multi-expression

aapost aapost at idontexist.club
Fri Mar 10 18:46:40 EST 2023


On 3/9/23 15:25, Thomas Passin wrote:
>  >>> # this is a code snippet from a Tkinter gui app
>  >>> # in this case lambda is quite convenient
>  >>> self.btn_cancel = Button(self.progress_container, text='Cancel',
>  >>>     command=lambda: subprocess.call('taskkill /f /im uberzip.exe',
>  >>>     shell=True))

> def kill_uberzip():
>      """Kill an external running program named uberzip.exe."""
>      subprocess.call('taskkill /f /im uberzip.exe', shell=True))
> 
> self.btn_cancel = Button(self.progress_container, text='Cancel',
>       command = kill_uberzip())
> 
> This way, it's easy to understand what btn_cancel() will do each time 
> you scan that line of code.  Using the lambda makes you reparse the line 
> and spend mental effort each time you scan it.  And this way, you know 
> directly that the button is going to cause a side effect outside your 
> program, which you have to infer (an indirect mental operation) when you 
> scan the lambda.
> 
> For this particular example, it might turn out that there could be more 
> than one instance of uberzip.exe running at the same time.  Which one 
> should be killed, and how do you kill the right one?  With the function, 
> you can get those details under control, but I hate to think what might 
> happen to the lambda expression.
> 
> Yes, of course, there can be times when the lambda expression is 
> somewhat easy to understand and the side effects are harmless.  In that 
> case, it may be easy enough to grasp quickly that the anonymous function 
> would not benefit from having a name.  So OK, it's not a hard-and-fast 
> rule.

The not knowing which uberzip to kill is a design choice, the bad design 
of the the example is not really the fault of the lambda.

And in general with naming anything, much has to do with context. 
Buttons perform actions, it should be implicitly understood at the 
button level and in context of where it is placed what it is a button is 
for. A button without a command has no use, so it is understood it needs 
to do something, with good design it would be better to know what the 
buttons purpose is, rather than having to then parse an additional 
function to figure it out.


Case in point, to solve the subprocess problem, (ignoring the "whys" and 
the lack of usefulness of this example, just grok the pattern, maybe you 
are designing a game of whack-a-mole and are watching 4 things and need 
to do something when you see a change from 1 of them)


import tkinter as tk
import subprocess

main = tk.Tk()
main.pids = {
     "messages": None,
     "syslog": None,
     "kern": None,
     "user": None,
}

tk.Button(
     master=main,
     text="Start tailing logs",
     command=lambda: (
		main.pids.update({"messages" :subprocess.Popen(["tail", "-n", "1", 
"-f", "/var/log/messages"])}),
		main.pids.update({"syslog" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/syslog"])}),
		main.pids.update({"kern" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/kern.log"])}),
		main.pids.update({"user" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/user.log"])}),
	),
).pack()

tk.Button(
     master=main,
     text="Kill messages tail",
     command=lambda: (
         main.pids["messages"].kill() if main.pids["messages"] else None,
	),
).pack()

tk.Button(
     master=main,
     text="Kill syslog tail",
     command=lambda: (
         main.pids["syslog"].kill() if main.pids["syslog"] else None,
	),
).pack()

tk.Button(
     master=main,
     text="Kill kern tail",
     command=lambda: (
         main.pids["kern"].kill() if main.pids["kern"] else None,
	),
).pack()

tk.Button(
     master=main,
     text="Kill user tail",
     command=lambda: (
         main.pids["user"].kill() if main.pids["user"] else None,
	),
).pack()

tk.Button(
     master=main,
     text="Kill all tails",
     command=lambda: (
		main.pids["messages"].kill() if main.pids["messages"] else None,
		main.pids["syslog"].kill() if main.pids["syslog"] else None,
		main.pids["kern"].kill() if main.pids["kern"] else None,
		main.pids["user"].kill() if main.pids["user"] else None,
	),
).pack()

main.mainloop()




More information about the Python-list mailing list