Decorating on function defined within loop [duplicate] - python

This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 1 year ago.
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...)

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.

You need to keep the reference 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)

Related

Trying to understand in Python 3.7 how to get a number returned from a function to the main program

In general terms, I cannot get any function to return a value inside a variable (an integer, back to the main program. I have included the variable name as one of 2 arguments in the function (in this case mod_stone_value), and am using the return command to hopefully return that integer value to the main program, where that value is then added to a total value.
The key lines of code in the function would be:
def calc_real_stone_value(base_stone_value, mod_stone_value):
return mod_stone_value
and then, back in the main program:
total_stone_value = total_stone_value + mod_stone_value
The variable total_stone_value ends up being 0, yet non-zero values of mod_stone_value do print inside the function. I know I am doing something fundamentally wrong, but have no idea what it is.
If I understand correctly, you want to use the number value returned by the function calc_real_stone_value and are confused as to why it is not changing after you call your function which supposedly "updates it's value".
The fundamental thing you are misunderstanding is that primitive data types (double, int, char, etc.) are passed by value, not by reference. Therefore, if you make changes to the local variable mod_stone_value within the function, the changes will not show in the variable that exists outside the function. If you want to use the value you are returning with the line return mod_stone_value you need to call the function with the variables you want to be used in the calculations and then assign that value to a variable (mod_stone_value in this case).
Example:
base_stone_value = 1
mod_stone_value = 4
def calc_real_stone_value(base_stone_value, mod_stone_value):
# calculations
# e.g. add the two integer arguments
mod_stone_value += base_stone_value
return mod_stone_value
# print the values for mod_stone_value
print(mod_stone_value)
# call function and assign value to mod_stone_value
mod_stone_value = calc_real_stone_value(base_stone_value, mod_stone_value)
print(mod_stone_value)
Output:
4
5
Conclusion:
Primitive variables are passed by value and in order to retrieve the value from a function that returns an integer for example, it needs to be re-assigned.
To reduce confusion, you can avoid using variable names that shadow variable names from an outer scope.
Using your code as a template, here is an example that illustrates passing a value from "inside" the function to the outer level (here, the outermost level of the program). I would recommend reading this part of the official Python tutorial (or from the beginning if it does not make sense) as it describes concepts (e.g. global vs. local variables) that are relevant to your problem.
c = 0
d = 5
e = 2
def foo(a, b):
# insert whatever code here
d = 10
print(f"Inside foo(), d has value {d}")
# to match your example
return b
# c has the value 0, d has the value 5
print(f"(c, d) has the value ({c}, {d})")
# call function and pass return value to c
c = foo(d, e)
# c has the value 2, d has the value 5
print(f"(c, d) has the value ({c}, {d})")
By the way, regarding your code, statements inside the body of the function should be indented. This includes the return statement.

How to iterate over functions with a different number of arguments

I want to iterate over a number of functions that are stored in a dictionary as :
function_dict = {'func_a': partial(func_a),
'func_b': partial(func_b)}
Let's say for simplicity that these functions are:
def func_a(x,y):
return x + y
def func_b(x):
return x**2
In fact, these functions are part of an external library, so I cannot modify them.
I want to be able to loop through all of them. However, func_a receives 2 arguments while func_b receives 1 argument.
I want to do the following:
x = 2
y = 5
for key, func in function_dict.items():
if (key=='func_a'):
print( (func)(x,y) )
if (key=='func_b'):
print( (func)(x) )
which yields
7
4
In this loop I have used the if-statement to see which function is coming up and use the appropriate number of arguments. But this is unusable if there are too many functions in the function_dict.
How can i standardize it so that I can loop through all the functions in the dictionary and pick up the correct number of arguments for each of them ?
Many thanks in advance.

Python: Using variables and content established in the main module without passing them as an explicit argument in the function definition

I am relatively new to python (and programming). I would like to understand what items, content, variables need to be passed as an explicit argument in the function definition and what can be used without being passed as an argument.
It seems that if a variable or item is introduced/defined in the module where the function is later located that it does not need to be passed as an argument.
Perhaps the below example from Codecademy will help to illustrate my question. Here we access the values from the created dictionaries stock and prices without passing either as an argument.
shopping_list = ["banana", "orange", "apple"]
stock = {
"banana": 6,
"apple": 0,
"orange": 32,
"pear": 15
}
prices = {
"banana": 4,
"apple": 2,
"orange": 1.5,
"pear": 3
}
def compute_bill(food):
total = 0
for item in food:
if stock[item] > 0:
total += prices[item]
stock[item] -= 1
return total
You can define a variable first, and then pass it to the function or loop just to initialize the variable ahead of time:
For instance:
i = 0
for i in some_list:
do something
i += 1
This will allow you to create i to 0 first and then loop through a list of an undetermined length and increment i to count over each item. If you were to try to run this function again however, it would do nothing as i has been increased and stays that way. You would need to reset i back to 0 or close out the interpreter and re-run the program.
Functions can do many different things. Say you want to say hello to someone:
def hello(name):
name = name
print(Hello, {}.format(name))
hello("Peter")
Output would be:
Hello, Peter
With this you can pass a variable without defining it, and it will store it inside of that function. If you try and call the "name" variable outside of the function though, you will get an error as its scope is only for that function. I hope this makes since and I highly recommend reading up more on Python Scope on their website here https://docs.python.org/3/tutorial/index.html This will tell you everything you need to know about functions in python.
Also as an aside, the reason the dictionaries are not passed as an argument in your example, is because we are using a For loop to iterate over the values. Look into For loops at the link I provided. Basically it is counting over each loop through and grabbing the prices of each item, and dynamically passing the location to each item via the "item" variable. once it reaches the end of the dictionary, it is giving you a total price, along with deducting inventory off the stock dictionary. So they are being used, but for a For loop in python there is no need to pass them as parameters. Now if you were to compare the two together, and your function was written in a way to take two dictionaries or strings as values to compare, then you would either need to define them in the function parameter itself, or create them ahead of time and pass the variable as a parameter.
Clarification:
So say you want to compare whats different in two dictionaries as a function. You can pass the dictionaries like so as parameters:
d1 = {'a': 912,'b':256,'c':350}
d2 = {'b':256,'x':290,'a':912}
def compare(a, b):
diff1 = set(a.items()) - set(b.items())
diff2 = set(b.items()) - set(a.items())
print(diff1)
print(diff2)
compare(d1,d2)
Output would be:
{('c', 350)}
{('x', 290)}
As you can see, we defined the dictionaries first, then passed them as arguments to the function to see what was different between the two.
You can even define the dictionaries on the fly:
compare({'a': 912, 'b': 256, 'c': 350}, {'b': 256, 'x': 290, 'a': 912})
Results remain the same.
You can also explicitly define they are used in the function directly but will limit the ability to use this function on any other dictionaries you wanted to compare.
d1 = {'a': 912,'b':256,'c':350}
d2 = {'b':256,'x':290,'a':912}
def compare():
diff1 = set(d1.items()) - set(d2.items())
diff2 = set(d2.items()) - set(d1.items())
print(diff1)
print(diff2)
compare()
Output remains the same as above. As you can see, we pass no arguments, and simply just compare the results and call that through:
compare()
It makes more sense though to pass arguments in this case, so you can compare multiple dictionaries anywhere in your program. Hope that clears that up some.
You can use python classes to initialize variables in the constructor and use them in the methods if you have many methods . So you can avoid passing same arguments to all functions
https://docs.python.org/3/tutorial/classes.html
In order to know how those list and dictionary is accessible in function defined later, you should know how python looks and resolves variables.
Look for local variable with same name if found use it
If no local variable is found then look for global variable if found then use it.
If global variable is not found then look for environment variable.(in sys.path) if found use that.
If variable doesn't exist then raise an error.
Looking for a variable is also like looking for a function . It is because of that you can also import python variables like importing a function. I hope you know this.

pass dict by reference to bit encoded list of functions

I seem to have an issue with a Manager.dict() that gets passed to a list of functions (within a sub process) as when I modify it within the function, the new value isn't available outside.
I create my list of functions like this:
gwfuncs = [reboot, flush_macs, flush_cache, new_gw, revert_gw, send_log]
gw_func_dict = dict((chr(2**i), gwfuncs[i]) for i in xrange(0,min(len(gwfuncs),8)))
and then call it like this:
for bit in gw_func_dict.keys():
if gwupdate & ord(bit) == ord(bit):
gw_func_dict[bit](fh, maclist)
Now assume we're talking about flush_macs(), whatever I do in the function to maclist, doesn't seem to be affecting the maclist outside of my function - why is this? How can I modify it the way that my changes are available outside?
== has higher precedence than &, so your if statement really acts like this:
if gwupdate & (ord(bit) == ord(bit)):
Add some parentheses and it'll work:
if (gwupdate & ord(bit)) == ord(bit):
Also, you can simplify your code a little:
gw_func_dict = dict((chr(2**i), func) for i, func in enumerate(gwfuncs[:8]))
And if you're using Python 2.7+:
gw_func_dict = {chr(2**i): func for i, func in enumerate(gwfuncs[:8])}
Also, iterating over a dictionary iterates over its keys by default, so you can remove .keys() from your for loop:
for bit in gw_func_dict:

How do I pass lots of variables to and from a function in Python?

I do scientific programming, and often want to show users prompts and variable pairs, let them edit the variables, and then do the calulations with the new variables. I do this so often, that I wrote a wxPython class to move this code out of the main program. You set up a list for each variable with the type of the variable (string, float, int), the prompt, and the variable's current value. You then place all of these lists in one big list, and my utility creates a neatly formated wxPython panel with prompts and the current values which can be edited.
When I started, I only had a few variables, so I would write out each variable.
s='this is a string'; i=1; f=3.14
my_list=[ ['s','your string here',s], ['i','your int here',i], ['f','your float here'],]
input_panel = Input(my_list)
# the rest of the window is created, the input_panel is added to the window, the user is
# allowed to make choices, and control returns when the user hits the calculate button
s,i,f = input_panel.results() # the .results() function returns the values in a list
Now I want to use this routine for a lot of variables (10-30), and this approach is breaking down. I can create the input list to the function over multiple lines using the list.append() statements. When the code returns from the function, though, I get this huge list that needs to be unpacked into the right variables. This is difficult to manage, and it looks like it will be easy to get the input list and output list out of sync. And worse than that, it looks kludgy.
What is the best way to pass lots of variables to a function in Python with extra information so that they can be edited, and then get the variables back so that I can use them in the rest of the program?
If I could pass the variables by reference into the function, then users could change them or not, and I would use the values once the program returned from the function. I would only need to build the input list over multiple lines, and there wouldn't be any possiblity of the input list getting out of sync with the output list. But Python doesn't allow this.
Should I break the big lists into smaller lists that then get combined into big lists for passing into and out of the functions? Or does this just add more places to make errors?
The simplest thing to do would be to create a class. Instead of dealing with a list of variables, the class will have attributes. Then you just use a single instance of the class.
There are two decent options that come to mind.
The first is to use a dictionary to gather all the variables in one place:
d = {}
d['var1'] = [1,2,3]
d['var2'] = 'asdf'
foo(d)
The second is to use a class to bundle all the arguments. This could be something as simple as:
class Foo(object):
pass
f = Foo()
f.var1 = [1,2,3]
f.var2 = 'asdf'
foo(f)
In this case I would prefer the class over the dictionary, simply because you could eventually provide a definition for the class to make its use clearer or to provide methods that handle some of the packing and unpacking work.
To me, the ideal solution is to use a class like this:
>>> class Vars(object):
... def __init__(self, **argd):
... self.__dict__.update(argd)
...
>>> x = Vars(x=1, y=2)
>>> x.x
1
>>> x.y
2
You can also build a dictionary and pass it like this:
>>> some_dict = {'x' : 1, 'y' : 2}
>>> #the two stars below mean to pass the dict as keyword arguments
>>> x = Vars(**some_dict)
>>> x.x
1
>>> x.y
2
You may then get data or alter it as need be when passing it to a function:
>>> def foo(some_vars):
... some_vars.z = 3 #note that we're creating the member z
...
>>> foo(x)
>>> x.z
3
If I could pass the variables by reference into the function, then users could change them or not, and I would use the values once the program returned from the function.
You can obtain much the same effect as "pass by reference" by passing a dict (or for syntactic convenience a Bunch, see http://code.activestate.com/recipes/52308/).
if you have a finite set of these cases, you could write specific wrapper functions for each one. Each wrapper would do the work of building and unpacking lists taht are passed to the internal function.
I would recommend using a dictionary
or a class to accumulate all details
about your variables
value
prompt text
A list to store the order in which you want them to be displayed
Then use good old iteration to prepare input and collect output
This way you will only be modifying a small manageable section of the code time and again.
Of course you should encapsulate all this into a class if your comfortable working with classes.
"""Store all variables
"""
vars = {}
"""Store the order of display
"""
order = []
"""Define a function that will store details and order of the variable definitions
"""
def makeVar(parent, order, name, value, prompt):
parent[name] = dict(zip(('value', 'prompt'), (value, prompt)))
order.append(name)
"""Create your variable definitions in order
"""
makeVar(vars, order, 's', 'this is a string', 'your string here')
makeVar(vars, order, 'i', 1, 'your int here')
makeVar(vars, order, 'f', 3.14, 'your float here')
"""Use a list comprehension to prepare your input
"""
my_list = [[name, vars[name]['prompt'], vars[name]['value']] for name in order]
input_panel = Input(my_list)
out_list = input_panel.results();
"""Collect your output
"""
for i in range(0, len(order)):
vars[order[i]]['value'] = out_list[i];

Categories