I believe this is a similar case, however, I'm not sure how to apply the solutions to my case.
I am creating a function within a loop that is supplied by an argument. The function is called later, but it is stored for later and is supplied with the argument.
for type_str in ["type_1", "type_2", "type_3"]:
@decorator_exec.register(...)
def to_exec(num: int):
print(type_str, num)
# later...
# this is an example
i = 0
for func in decorator_exec.funcs:
func(i)
i
Unfortunately, this is what would be produced:
type_3 0
type_3 1
type_3 2
I would like the output to ensure that the type_str variable (and any other variables that are included in to_exec() body), be what it should be when it is defined.
type_1 0
type_2 1
type_3 2
The to_exec() that is within the loop isn't used later within the loop, or outside the loop. It is solely stored by the decorator code and is called from its stored functions later.
If it isn't an XY problem, a possible solution would be to use a function factory (I'm not sure how that would work...)
CodePudding user response:
You are not storing the functions for later, you are assigning three functions to the same name, so the last one is what sticks: the previous two get overwritten by the next one.
You can achieve what you're looking for with something like this:
# This is a dictionary created with a dict comprehension.
functions = {
# A decorator is just a function that takes a function and
# returns a function, so you can call it like a normal
# function. The first argument is your own function,
# and the result will be the decorated function.
type_str: decorator_exec.register(
# This is an anonymous function. You don't need to give
# a name to the function in this case because it will
# be called with the dict key it's stored in anyway.
lambda num: print(type_str, num),
..., # Any arguments the decorator needs
)
for type_str in ["type_1", "type_2", "type_3"]
}
This will create a dictionary with one entry per string in your list, such that functions["type_1"] will have the function defined with "type_1" and so on, which you can call, for example, like this: functions["type_2"](420).
You could achieve the same by adding each entry to an empty dict using a for loop, but comprehensions are usually more idiomatic. Because of the decorator, in this case it's probably more readable to do it the wordy way (and it allows you to use type annotations too):
functions = {}
for type_str in ["type_1", "type_2", "type_3"]:
@decorator_exec.register(...)
def the_func(num: int):
print(type_str, num)
functions[type_str] = the_func
The key takeaway is that you cannot use the same name more than once and expect different results, so you have to store your functions in a data structure. If you don't care about the name of the functions at all, you can use a list instead of a dictionary, and simply retrieve/call each function with its index.
CodePudding user response:
You need to keep the refference to the correct string in memory. you can do that by accessing the items with an index variable.
The 2 first lines are relevant for this.
The rest is just to provide a working example,
def generate_funcs(xs, i=0):
return [lambda x: print(xs[i], x)] generate_funcs(xs, i 1) if i<len(xs) else []
f = generate_funcs(["type_1", "type_2", "type_3"])
for (func, i) in zip(f, range(len(f))):
func(i)
