Adding Arguments to Function Objects with Curry (Python)

vegaseat 1 Tallied Votes 187 Views Share

There are situations in Python code when a function is used as an object rather then a function call. Handling arguments becomes a mild problem, that can easily be handled by wrapping the function object and its arguments. One common way is the use of a curry function or class. This snippet shows the use of a curry function in a short Tkinter GUI dialog demo. Here the function objects appear in a dictionary of dialogs and the button command associated with a specific dialog.

# use Tkinter to create a series of buttons that associate with dialog objects
# handle arguments to and from the dialog objects with function curry()
# tested with Python24       vegaseat        19sep2006

from Tkinter import *
from tkFileDialog   import askopenfilename
from tkMessageBox   import askquestion, showerror, askokcancel
from tkSimpleDialog import askfloat, askinteger, askstring
from tkColorChooser import askcolor

root = Tk()

# most efficient generic function for currying
def curry(fn, *cargs, **ckwargs):
    def call_fn(*fargs, **fkwargs):
        d = ckwargs.copy()
        d.update(fkwargs)
        return fn(*(cargs + fargs), **d)
    return call_fn

# since this is a dictionary, buttons pack in a hashed order
# the dictionary uses function objects so you have to wrap the object and its arguments
dialogs = {
    'Open':  askopenfilename,    # returns eg. D:/Python24/Atest/tk/Tk_DialogDemo1.pyw
    'Color': askcolor,           # returns eg. ((0, 0, 255), '#0000ff') if you picked color blue
    'Question': curry(askquestion, 'Warning', 'Erase zz.txt file?'),  # returns 'yes' or 'no'
    'Error': curry(showerror, 'showerror', "Stupid error!"),          # returns 'ok'
    'Input_F': curry(askfloat, 'askfloat', 'Enter price of petrol'),  # returns floating point value
    'Input_I': curry(askinteger, 'askinteger', 'Enter your age'),     # returns an integer value
    'Input_S': curry(askstring, 'askstring', 'Enter your name'),      # returns a string
    'Query': curry(askokcancel, 'askokcancel', 'Okay to proceed?')    # returns True or False (1 or 0)
}

class MyApp(Frame):
    """create a frame with two labels and a series of buttons"""
    def __init__(self, parent=None):
        Frame.__init__(self, parent)
        self.pack()
        self.label1 = Label(self, text="Click to bring up dialog")
        self.label1.pack()
        for (key, value) in dialogs.items():
            # sets up the button and associates the proper dialog object
            callback = curry(self.dialog_result, name=key)
            Button(self, text=key, command=callback).pack(side=TOP, fill=BOTH)
        Button(self, text='Quit', command=root.destroy).pack(side=TOP, fill=BOTH)
        self.label2 = Label(self, text="result", bg='yellow')
        self.label2.pack()

    def dialog_result(self, name):
        # connect dialog name/key with its result
        result = dialogs[name]()
        # show result in label2
        self.label2.config(text=result)
        # to test, set label1 background to the color you picked
        if name == 'Color':
            color = result[1]  # uses hex_color = dialogs['Color']()[1]
            self.label1.config(bg=color)


if __name__ == '__main__':
    app = MyApp()
    app.mainloop()
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.