How to use a copy of a variable in a closure [duplicate] - python

This question already has answers here:
Python Argument Binders
(7 answers)
Closed 2 years ago.
In python I can use the nonlocal keyword to make sure that I use the variable from the outer scope, but how do I copy the variable from the outer scope into the closure?
I have code like this:
my_functions_with_parameters = []
for fun_name in my_function_names:
fun = my_functions[fun_name]
def fun_with_parameters():
return fun(1,2,3)
my_functions_with_parameters.append(fun_with_parameter)
My problem is, that each instance of fun_with_parameters calls the last function, i.e., my_functions[my_function_names[-1]], because the variable fun is a reference.
I tried to use fun = copy.deepcopy(my_functions[fun_name]) but it did not change anything and is probably not the best or fastest way.

Try this instead:
my_functions_with_parameters = []
for fun_name in my_function_names:
fun = my_functions[fun_name]
my_functions_with_parameters.append(fun)
Now you can provide the parameters to each fun in my_functions_with_parameters when you loop through them:
for my_fun in my_functions_with_parameters:
my_fun(1,2,3)
Or you can store the parameters with the function in a tuple:
my_functions_with_parameters = []
for fun_name in my_function_names:
fun = my_functions[fun_name]
my_functions_with_parameters.append((fun, 1, 2, 3))
And call via:
for tup in my_functions_with_parameters:
tup[0](tup[1], tup[2], tup[3])

Related

Is there a bug in numpy when using the += operator [duplicate]

This question already has answers here:
What is the difference between i = i + 1 and i += 1 in a 'for' loop? [duplicate]
(6 answers)
When is "i += x" different from "i = i + x" in Python?
(3 answers)
Understanding Python's call-by-object style of passing function arguments [duplicate]
(3 answers)
How do I pass a variable by reference?
(39 answers)
Closed 3 years ago.
When using a function in phyton, I am taught that actually a copy of my value is parsed. In the example shown below apply a function onto a Parameter a. Here, I expected that a copy of a is sent to the function fun. In this function, only the copy of a is available, not parameter a on a global scope. I even gave it another Name: b. When I modify the value b in my function, then also the Parameter a on the global scope is changed. Is this supposed to be correct?
import numpy as np
def fun(b):
b += np.array([1,1])
a = np.array([1,1])
fun(a)
print(a)
I expected to get np.array([1,1]), but I get np.array([2,2])
This happens only, when I use the += Operator in the function fun. If I use b = b + np.array([1,1]) instead, the value of a on global scope stays the same.
When you call fun() you don't make a copy of a and modify b, rather, a itself is passed to fun() and refered to as b in there.
As such,
b += np.array([1,1])
modifies a.
In python the list datatype is mutable, that means that every time you run your function, a list will keep growing.
The key moment is here: b += np.array([1,1])
You already got a, which is [1, 1] and you adding it to another array which is [1, 1] and you are getting [2, 2] which is how it should be.
You are ending up modifying the a

Is list pass by value or by reference? [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 4 years ago.
Consider the following code, at first glance it does the same thing, but the result is different, sometimes it seems list is pass by value, sometimes list seems pass by reference:
lst = [1, 2]
def f(lst):
# lst = lst + [3] # seems pass by value
# lst += [3] # strange! same as above but seems pass by reference
lst = lst.append(3) # seems pass by reference
return lst
f(lst)
print(lst)
can anyone tell me what is going on?
It’s passed by value of reference. So modifications to the object can be seen outside the function, but assigning the variable to a new object does not change anything outside the function.
It’s essentially the same as passing a pointer in C, or a reference type in Java.
The result of the += case is because that operator actually modifies the list in place, so the effect is visible outside the function. lst.append() is also an in-place operation, which explains your last case.

Is it possible to make a function that modifies the input [duplicate]

This question already has answers here:
Python functions call by reference [duplicate]
(12 answers)
Closed 6 years ago.
makebbb(a):
a = "bbb"
this function obviously fails to convert it's input to "bbb" as demonstrated by the following snippet
x = "notbbb"
makebbb(x)
print(x)
# Outputs "notbbb"
Now I understand the reason this fails, what I wish to find out is if there is anyway to make such a function? (I'm not just talking about assigning strings, but assignment with = in general)
Define the variable you want to change from a func as global ,
>>> def makebbb():
global x
x = "bbb"
>>> print x
notbbb
>>> makebbb()
>>> print x
bbb
>>>
why not simply return a value in your function and update 'a'?
def makebbb(a):
return "bbb"
x = "notbbb"
x = makebbb(x)
print(x)
def makebbb(a):
return "bbb"
x = "notbbb"
x = makebbb(x) #call function in the assignment to modify it
print(x)
Output:
bbb
If x exists outside the scope of the function, you can modify the variable directly (rebind values to the same variable in memory). Otherwise, a functional approach would be to just return a new string.
x = "notbbb"
makebbb() # Modify variable outside function scope.
print(x)
or
x = "notbbb"
print(makebbb(x)) # return new value.
The particular catch with this example is that you are trying to pass a an immutable value by reference. What happens instead is that a copy is created on argument passing. You can achieve the effect you want with mutable types such as lists though.
You can modify some arguments, those that are passed by reference instead of by value.
Arguments that are mutable objects can be changed, like so:
def change_list(l):
l.append(4)
k = [1, 2, 3]
change_list(k)
print(k) # Prints [1,2,3,4]
This, however, will not work:
def dont_change_list(l):
l = [5]

How to get a closure to refer to the value that a variable had at the time of its definition [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Creating lambda inside a loop
In the code below, invoking any member of the returned array of closures
prints the number 4.
def go():
x = []
for i in range(5):
def y(): print i
x.append(y)
return x
I would like each member of the closure to print the number that i was when the closure was defined.
One way around this is to use default arguments:
def y(i=i):
print i
Default arguments are evaluated when the function is created, not called, so this works as you'd expect.
>>> i = 1
>>> def y(i=i): print i
...
>>> i = 2
>>> y()
1
A little extra info just for fun:
If you're curious what the defaults are, you can always inspect that with the .func_defaults attribute (__defaults__ in python3.x):
>>> y.func_defaults
(1,)
This attribute is also writeable, so you can in fact change the defaults after the function is created by putting a new tuple in there.

How can I force Python to create a new variable / new scope inside a loop? [duplicate]

This question already has answers here:
Closed 10 years ago.
Today I explored a weird behavior of Python. An example:
closures = []
for x in [1, 2, 3]:
# store `x' in a "new" local variable
var = x
# store a closure which returns the value of `var'
closures.append(lambda: var)
for c in closures:
print(c())
The above code prints
3
3
3
But I want it to print
1
2
3
I explain this behavior for myself that var is always the same local variable (and python does not create a new one like in other languages). How can I fix the above code, so that each closure will return another value?
The easiest way to do this is to use a default argument for your lambda, this way the current value of x is bound as the default argument of the function, instead of var being looked up in a containing scope on each call:
closures = []
for x in [1, 2, 3]:
closures.append(lambda var=x: var)
for c in closures:
print(c())
Alternatively you can create a closure (what you have is not a closure, since each function is created in the global scope):
make_closure = lambda var: lambda: var
closures = []
for x in [1, 2, 3]:
closures.append(make_closure(x))
for c in closures:
print(c())
make_closure() could also be written like this, which may make it more readable:
def make_closure(var):
return lambda: var
You can't create a new variable in the local scope inside the loop. Whatever name you choose, your function will always be a closure over that name and use its most recent value.
The easiest way around this is to use a keyword argument:
closures = []
for x in [1, 2, 3]:
closures.append(lambda var=x: var)
In your example, var is always bound to the the last value of the loop. You need to bind it inside the lambda using closures.append(lambda var=var: var).

Categories

Resources