[Python-ideas] Explicit variable capture list

Terry Reedy tjreedy at udel.edu
Tue Feb 16 16:07:34 EST 2016


On 1/25/2016 1:52 PM, Guido van Rossum wrote:
> On Sun, Jan 24, 2016 at 10:32 PM, Terry Reedy <tjreedy at udel.edu> wrote:
>> What I've concluded from this thread is that function definitions (with
>> direct use 'def' or 'lambda') do not fit well within loops, though I used
>> them there myself.
>
> Right. When you can avoid them, you avoid extra work in an inner loop,
> which is often a good idea.
>
>> When delayed function calls are are needed, what belongs within loops is
>> packaging of a pre-defined function with one or more arguments within a
>> callable.  Instance.method is an elegant syntax for doing so.
>> functools.partial(func, args, ...) is a much clumsier generalized
>> expression, which requires an import.  Note that 'partial' returns a
>> function for delayed execution even when a complete, not partial, set of
>> arguments is passed.
>
> Right. I've always hated partial() (which is why it's not a builtin)
> because usually a lambda is clearer (it's difficult to calculate in
> your head the signature of the thing it returns from the  arguments
> passed), but this is one thing where partial() wins, since it captures
> values.

I agree that the difficulty of immediately grokking the signature 
partials that binds arbitrary parameters is a downside to their use.

Fake parameters set to a constant necessarily go at the end of the 
parameter list.  The actual signature is the list with those parameters 
chopped off and ignored.  To eliminate the possibility of accidentally 
supplying a different value positionally, such parameters could (and I 
think should) be made keyword-only.

def f(a, b='default', *, int=int): pass

Bound methods necessarily bind the first parameter, often called 'self'. 
  This again makes the actual signature easy to determine.

>> A major attempted (and tempting) use for definitions within a loop is
>> multiple callbacks for multiple gui widgets, where delayed execution is
>> needed.  The three answers to multiple 'why doesn't this work' on both
>> python-list and Stackoverflow are multiple definitions with variant 'default
>> args', a custom make_function function outside the loop called multiple
>> times within the loop, and a direct function outside the loop called with
>> partial within the loop.  I am going to start using partial more.

Since writing this, I realized that defining a custom class and using 
bound methods is a fourth option, which I also like.  This binds the 
differentiating data to an instance, which is then bound to the 
function, rather than to the function directly.  A toy example:

----
import tkinter as tk
root = tk.Tk()

class Card(tk.Button):
     hide = 'XXX'
     def __init__(self, txt):
         tk.Button.__init__(self, root, text=self.hide)  # or
         # super().__init__(root, text=self.hide)
         self.txt = txt
         self.exposed = False
     def flip(self):
         self['text'] = self.hide if self.exposed else self.txt
         self.exposed = not self.exposed

for i, txt in enumerate(('one', 'two')):
     card = Card(txt)
     card['command'] = card.flip
     card.grid(row=0, column=i)

#root.mainloop()  # uncomment if run on command line without -i
----

The main problem with this is that some beginners are trying to write 
(or being told to write) tkinter guis before they learn about class 
statements.  The super() form is easier to write, but its use is even 
more 'advanced'.

-- 
Terry Jan Reedy



More information about the Python-ideas mailing list