On 8/17/2016 2:07 AM, Steven D'Aprano wrote:

I realise that there are occasions where we might deliberate choose to assign
an intermediate value to its own variable, but all else being equal, which
would you prefer?

#A
alist = []
alist.append(2)
alist.append(4)
alist.append(8)
process(alist)

#B
process([2, 4, 8])

#A
value = 0
for i in range(100):
    value += 1
process(value)

#B
process(100)

#A
tmp = get_some_string()
s = tmp[1]
s += tmp[2]
s += tmp[3]
process(s)

#B
process(get_some_string()[1:4])

Up to here, #A is a useless and stupid.  Have you seen such code written?

#A
def callback(btn):
    return btn.do_the_thing(42) or default
the_button.setcommand(callback)
process(the_button)

#B
the_button.setcommand(lambda btn: btn.do_the_thing(42) or default)
process(the_button)

This example is *not* parallel to the other 3. Here, A is useful real code and might be preferred for multiple reasons.

If you find yourself preferring B, B, B, A, you might ask yourself what makes a
function different that you prefer to keep temporary functions around where
they're not needed.

When 'callback' is set as the command of the button, it is not temporary, but must remain as long as the button remains. Only the name binding is (possibly) disposable.

One may want the function to have a meaningful name that says what it does.

One may want the function to have a name for tracebacks.

In a framework that passes the button to button callbacks (not tk, unfortunately), the same callback might be used for multiple buttons. (In tk, one must create a wrapper of callback for each button.)

The example is misleading in that the return value of a Button callback is likely irrelevant. (This is true of all tkinter/tk callbacks that I can think of.) More realistic is

#A
def callback(btn):
    btn.do_the_thing(42)
the_button.setcommand(callback)

#A'
def cb(btn): btn.do_the_thing(42)
the_button.setcommand(cb)

#B
the_button.setcommand(lambda btn: btn.do_the_thing(42)

I have written code like #B, but #A is more correct, to me, in returning None instead of the ignored value of the call.

Beginners often do not understand that the body of a lambda expression is evaluated in a new local namespace, and only when the resulting function is called, the same as with a def statement. They then neglect to capture current values when writing lambda expressions in a for loop.

In many cases, there is a third alternative using functools.partial.

Production gui code is typically written with classes. This adds the possibility of using bound methods.

This example require the_button to be defined elsewhere. Typically, the command can and should be set when the button is defined, along with other options. Having 'cb' pre-defined may make the defining call more readable and the arg list fall on one line rather than two.


Here is a real example from idlelib/help.py

    def toc_menu(self, text):
        "Create table of contents as drop-down menu."
        toc = Menubutton(self, text='TOC')
        drop = Menu(toc, tearoff=False)
        for lbl, dex in text.parser.toc:
drop.add_command(label=lbl, command=lambda dex=dex:text.yview(dex))
        toc['menu'] = drop
        return toc

The local names 'lbl' and 'dex' were chosen short so that the long line would be exactly at the limit of 79 chars. Here is a rewrite of the for loop.

        for section_name, line_number in text.parser.toc:
            def goto(line=line_number):
                text.yview(line)
            drop.add_command(label=section_name, command=goto)

To me, this is much better and I intend to commit it. Thank you for prodding me to think through how bad the lambda form can be and to rewrite the loop so I don't cringe reading it.

--
Terry Jan Reedy


--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to