Lambda returning tuple question, multi-expression

Chris Angelico rosuav at gmail.com
Thu Mar 9 17:11:31 EST 2023


On Fri, 10 Mar 2023 at 07:43, Thomas Passin <list1 at tompassin.net> wrote:
>
> On 3/9/2023 3:29 AM, aapost wrote:
> > The 'what I am trying to do' is ask a question regarding opinions and
> > practices on issuing a sequence of actions within a lambda via a tuple
> > (since the common practice approaches against it - mainly with tkinter -
> > feel more convoluted), and in doing so leaving it open ended to get a
> > feel on what opinions are, and to see if any opinions influence mine.
>
> I finally realized why I am uncomfortable with doing this kind of thing.
>   It's because, IMHO, lambda expressions should not have side effects
> and should not require much mental effort to grasp what they do.  Yes,
> you can do so.  You can even print from a lambda (and it might be useful
> for debugging):
>
> lambda x: print(f'The lambda received {x}') or x*x

I'm not sure why lambda functions shouldn't have side effects. They
can be used for anything, as long as it's a single expression (which
can, of course, be composed of other expressions).

> The Python Reference on readthedocs.io also has a tk example that
> specifically wants the side effect (see
> https://python-reference.readthedocs.io/en/latest/docs/operators/lambda.html):
>
>  >>> # 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))
>
> Maybe so, but I think it's better not to have side effects hidden away
> in expressions that are hard to read and understand.  And being
> anonymous, there is no function name to remind you what the action is
> suppose to do. Much better (still IMHO, of course):

The purpose of the lambda function is to tie the effect to the
function. Otherwise, you have to go elsewhere to figure out what each
one does. It's not inherently better to give names to everything,
especially when those functions are only ever used once. In fact, I'd
say that that is usually *worse*.

> 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())

And this is one of the reasons it's worse: with the lambda function,
you won't fall into this trap. It's a classic bug, and a rather nasty
one; can you see it? If not, this is a VERY strong argument in favour
of the lambda function. Even if you can, the mere fact that you made
this error indicates how easy it is to slip into the faulty way of
writing it.

> This way, it's easy to understand what btn_cancel() will do each time
> you scan that line of code.

Only if the name is enough information, and it seldom is. Whereas with
the lambda function, you have the entire code there, so you can
understand what btn_cancel does without having to bookmark your spot
there, go elsewhere, and find out what kill_uberzip() does.

> 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.

Maybe, but this is just the example getting in the way. Obviously the
larger a function needs to be, the less reasonable to make it a lambda
function.

That's why I'm of the opinion that it's better to use a different
system, such as function decorators or a class namespace. For example:

self.btn_cancel = Button(...)

@on(self.btn_cancel, "command")
def kill_uberzip(): ...

Or alternatively:

class btn_cancel(Button):
    def command(self):
        ...

These kinds of systems allow much more flexibility than lambda
functions, without the issues of name repetition and disconnection.

> 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.

Definitely not a hard-and-fast rule, but in defense of the Python's
lambda, at least we don't have resonance cascades happening.

ChrisA


More information about the Python-list mailing list