简体   繁体   中英

Python3: use exec() to create a function

I am using tkinter to create an app and currently I made a lot of buttons so I need to bind all buttons with different command, I will like to use exec() to create functions.

strategy=None
exec("global commandbutton"+str(len(strategicpoint)+1)+"\ndef commandbutton"+str(len(strategicpoint)+1)+"():\n\tglobal strategy\n\tstrategy="+str(len(strategicpoint)))
commandline=eval('commandbutton'+str(len(strategicpoint)+1))
imgx=tk.Button(win,image=towert,command=commandline)

for cleaner solutions:

global commandbutton{...}
def commandbutton{...}():
    global strategy
    strategy={...}

I want my code run like above and it runs, but later I call the command and test to print(strategy) , (I have clicked the button/invoked the command) it prints None when I want it to print something else.

There is absolutely no need to use exec() or eval() here.

  • Functions don't have to be named sequentially. You can store function objects in a loop variable too and use that loop variable to create a tkinter hook.
  • You can create functions with a bound parameter without exec , using a closure, or just by binding the parameter in a lambda function or functools.partial() .

So if you have a loop with an incrementing strategicpoint value, I'd just do this:

def set_strategy(point):
    global strategy
    strategy = point

buttons = []
for strategicpoint in range(1, number_of_points + 1):
    imgx = tk.Button(win, image=towert, command=lambda point=strategicpoint: set_strategy(point))
    buttons.append(imgx)

The lambda point=... part binds the current loop value as a default value for the point argument of the new function object that lambda creates. When that function is called without arguments (as would be done when clicking the button), then the new function uses the integer value that was assigned to strategicpoint at the time, to call set_strategy(point) .

You can also use a closure, a local variable in an outer function, that an inner function makes use of. Nested, inner functions inside an outer function are created each time you call the outer function, so they are separate from other function objects created by the same outer function:

def create_strategy_command(strategypoint):
    def set_strategy():
        global strategy
        strategy = strategypoint
    return set_strategy

then when creating your buttons, use:

imgx = tk.Button(win, image=towert, command=create_strategy_command(strategicpoint))

Note that calling the create_strategy_command() function returns a new function here, used as the button command.

Disclaimer: I haven't tested this.

Use a dictionary to store all your functions, as such:

option = "default"
def change_option(target):
    global option
    option = target

def f1():
    print("foo")
def f2():
    print("bar")

my_functions = {
    "select1": f1,
    "select2": f2
    "default": None
    }

imgx=tk.Button(win,image=towert,command=my_functions[option])  # None
swapper = tk.Button(win, image=towert, lambda: change_option("select2")) # button to change the command if you want it
imgx=tk.Button(win,image=towert,command=my_functions[option]) # print("bar")
change_option("select1")
imgx=tk.Button(win,image=towert,command=my_functions[option]) # print("foo")

You could probably get by without using a dictionary, but in my opinion this is rather clean. Don't ever use exec() or eval(), unless you absolutely know what security concerns it has, you know the product wont be used on another machine, or you really don't have another choice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM