[Tutor] Shortening the code.
Peter Otten
__peter__ at web.de
Fri Nov 25 19:40:37 CET 2011
Mic wrote:
>
>> >from functools import partial
>
>>I use this kind of explicit import for a few names that I use frequently,
>>namely defaultdict, contextmanager, everything from itertools...
>>I think of these as my personal extended set of builtins ;)
>
>>As to the actual partial() function, you probably don't see it a lot
>>because
>>it has been in the standard library for only three years. The older idiom
>>for making a function that calls another function with a fixed argument is
>
> command = lambda button=button: button_clicked(button)
>
> Alright! What is a fixed argument?
Suppose you have a function add() that calculates the sum of two values
>>> def add(a, b):
... return a + b
...
>>> add(1, 2)
3
If you want another function that takes one argument and adds 2 to that
argument you can either start from scratch
>>> def plus_two(b):
... return 2 + b
...
>>> plus_two(7)
9
or build on your earlier work
>>> from functools import partial
>>> plus_two = partial(add, 2)
>>> plus_two(3)
5
The effect of wrapping add into partial here is that every time you call
plus_two(x) it will in turn call add(2, x). As the first argument is always
the same I called it "fixed", but it's not a terminus technicus.
>>You can iterate over (row, column) pairs instead of the dummy _ variable:
>
>>def create_widgets(self):
> >for row, column in [(0, 0), (1, 1), (2, 2), (3, 0)]:
> >button = tk.Button(self)
> >command = partial(button_clicked, button)
> >button["command"] = command
> >button.grid(row=row, column=column)
> >command()
>
> Very nice! I didn't know that it was possible. This saves me a lot of
> space. Am I, as a beginner, supposed to know this?
You may read it up somewhere or be shown on a mailinglist ;)
> Say that when button one is pressed I want a text file to be created. The
> text in the
> file should be the same number as the button I pressed. So if I press
> button one, once,
> it should create a text file with the name button_1. The text in the file
> should also be "button_1".
>
> If the button is pressed again, it should remove the file. Is this
> possible to do without
> any trouble?
>
> So if the second button is pressed , it should create a text file with the
> name button_2
> and the text in it should be button_2. If the button is pressed once
> again, it is supposed to
> delete that file it created.
> I also have one last question.
> How do I make the first button that is created to be named
> 1, the second to be created 2, the third 3 and so on?
Here's a modified script:
import tkinter as tk
from functools import partial
def button_clicked(button):
if button["bg"] == "green":
button["bg"] = "red"
print("deleting file", button.filename)
else:
button["bg"] = "green"
print("creating file", button.filename)
class Window(tk.Frame):
def __init__(self, master):
super (Window, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
for index in range(20):
button = tk.Button(self, text="Button {}".format(index+1),
bg="red")
button.filename = "button{}.txt".format(index+1)
command = partial(button_clicked, button)
button["command"] = command
row, column = divmod(index, 4)
button.grid(row=row, column=column)
root = tk.Tk()
root.title("Test")
app = Window(root)
root.mainloop()
I hope you can infer answers to your questions from the above...
To be honest, we've reached the point where I would switch to a
tkinter.Button subclass:
import tkinter as tk
FREE = "green"
OCCUPIED = "red"
class SeatButton(tk.Button):
def __init__(self, master, index):
text = "Button {}".format(index+1)
super(SeatButton, self).__init__(master, text=text, bg=FREE,
command=self.clicked)
self.filename = "button{}.txt".format(index+1)
self.occupied = False
def clicked(self):
self.occupied = not self.occupied
if self.occupied:
self["bg"] = OCCUPIED
print("creating file", self.filename)
else:
self["bg"] = FREE
print("deleting file", self.filename)
class Window(tk.Frame):
def __init__(self, master):
super (Window, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
for index in range(20):
button = SeatButton(self, index)
row, column = divmod(index, 4)
button.grid(row=row, column=column)
root = tk.Tk()
root.title("Test")
app = Window(root)
root.mainloop()
Too bad that now you've understood what partial() does it's gone...
> Why is it not recommended to to mix the styles if you put the respective
> imports at the
> beginning of the module?
Consistency. Either call the button tk.Button or Button.
More information about the Tutor
mailing list