[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¬es=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