[Tutor] Button command arguments: Using class to wrap function call versus using lambda in tkinter

Peter Otten __peter__ at web.de
Sun Apr 23 03:38:13 EDT 2017


boB Stepp wrote:

> Phil's recent postings have motivated me to try studying tkinter more
> systematically starting today, so I have been looking over available
> web resources.  Playing around with the code in one such resource, I
> was looking at a section entitled "Arguments to Callbacks" towards the
> bottom of the page at
> 
> 
http://userpages.umbc.edu/~dhood2/courses/cmsc433/spring2012/?section=Notes&topic=Python&notes=92
> 
> they have the code snippet:
> 
> ==========================================================================
> import Tkinter
> 
> 
> # command class to wrap function call with args
> # a.k.a. "currying"
> class Command:
>     def __init__(self, callback, *args, **kwargs):
>         self.callback = callback
>         self.args = args
>         self.kwargs = kwargs
> 
>     def __call__(self):
>         return apply(self.callback, self.args, self.kwargs)
> 
> 
> def callback(arg):
>     print "You called callback with the following arg: %s" % arg
> 
> 
> root = Tkinter.Tk()
> 
> Tkinter.Button(root, text="Foo", command=Command(callback, 'Foo')).pack()
> Tkinter.Button(root, text="Bar", command=Command(callback, 'Bar')).pack()
> Tkinter.Button(root, text="Baz", command=Command(callback, 'Baz')).pack()
> 
> root.mainloop()
> ==========================================================================
> 
> This is Python 2 code, so I endeavored to convert it to Python 3.  I
> came up with:
> 
> ==========================================================================
> #!/usr/bin/env python3
> 
> import tkinter as tk
> 
> # Command class to wrap function call with args
> # A.K.A. "currying"
> class Command:
>     def __init__(self, callback, *args, **kwargs):
>         self.callback = callback
>         self.args = args
>         self.kwargs = kwargs
> 
>     def __call__(self):
>         return self.callback(*self.args, **self.kwargs)
> 
> def callback(arg):
>     print('You called callback with the following arg:  %s' % arg)
> 
> root = tk.Tk()
> tk.Button(root, text='Foo', command=Command(callback, 'Foo')).pack()
> tk.Button(root, text='Bar', command=Command(callback, 'Bar')).pack()
> tk.Button(root, text='Baz', command=Command(callback, 'Baz')).pack()
> 
> root.mainloop()
> ==========================================================================
> 
> This seems to work fine, and got me to wondering about using lambda
> instead of this class wrapper approach, which gave me:
> 
> ==========================================================================
> import tkinter as tk
> 
> def callback(arg):
>     print('You called callback with the following arg:  %s' % arg)
> 
> root = tk.Tk()
> tk.Button(root, text='Foo', command=lambda arg='Foo':
> callback(arg)).pack() tk.Button(root, text='Bar', command=lambda
> arg='Bar': callback(arg)).pack() tk.Button(root, text='Baz',
> command=lambda arg='Baz': callback(arg)).pack()
> 
> root.mainloop()
> ==========================================================================
> 
> And this also works well.  Seeing as this approach has fewer lines of
> code and reads clearly, what advantages would the class wrapper
> approach have?  The only thing that is occurring to me in my current
> sleepy state is that the class wrapper is much more flexible with
> handling varying passed arguments.  Are there other considerations I
> should be aware of?

Generally I'd also prefer the lambda or functools.partial(callback, "Foo"), 
but if you want to maintain per-button state a class may be worth 
considering. A simple example:

class Callback:
    def __init__(self, template):
        self.count = 0
        self.template = template

    def __call__(self):
        self.count += 1
        print(self.template.format(self.count))


tk.Button(root, text='Foo', command=Callback("Foo pressed {} times")).pack()




More information about the Tutor mailing list