I am new to python. I have a few questions coming from an armature!

Terry Reedy tjreedy at udel.edu
Wed Aug 17 16:25:03 EDT 2016


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





More information about the Python-list mailing list