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

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

Related

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

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])

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.

Types inside a function in Python. Why some gets updated but not others? [duplicate]

This question already has answers here:
Immutable vs Mutable types
(18 answers)
Closed 5 years ago.
Why does calling several times in a row the following function:
a = []
def test(a,b):
if b > 0:
a.append(1)
return a
with test(a,4), it enlarges the list a each time, but calling several times in a row the function:
a = 0
def test(a,b):
if b > 0:
a += 1
return a
with test(a,4) returns 1 every single time instead of 1, 2, 3, etc.?
It looks like lists get updated by a function and retain their updated value even after the function finished to execute, while this behavior doesn't hold for integers (and I guess floats and several other types).
Integers are immutable; lists are mutable. a += 1 changes the value of a by reassigning the value it refers to. a.append(1) adds the value 1 to the list that a refers to, without changing the reference itself.
In your test function, a if a reference within the function's scope; not the same reference as a in the global scope. However, when passing a mutable object, the reference remains the same; allowing for the object to be modified without the need to reassign the variable. In your function
def test(a, b):
if b > 0:
a += 1
return a
The value of a is modified relative to test. To reassign the value globally, you need to perform that action in the global scope (or use the global keyword). So, instead of test(a, 4), use a = test(a, 4) to reassign the value of a.

How do you create new commands in Python when immutable data types are involved? [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 5 years ago.
In Python, the following code prints '0', not '1'.
def inc(x):
x = x+1
a = 0
inc(a)
print(a)
I understand why this happens; it's because integers are immutable. What I don't understand is how to get around this behaviour when it's undesirable. Suppose we want to create a new command such that the code
a = 0
inc(a)
print(a)
prints '1'.
Obviously, the naive approach won't do it. What can we do instead?
Similar (a bit more general) question can be found here along with a discussion how Python passes params to functions. In short, without making x variable in your code an object, I believe there's nothing we can do. Of course, you can alter your code to e.g. return changed value from function inc() and print that (i.e. print(inc(x))) or just do the printing from inside the inc() method, but that's not what you're essentially looking for.
If I understand correctly, You are trying to increment variable a using function inc(var) and passing 'a' as a external variable to the function inc().
As #Marko Andrijevic stated, variable x passed to function inc() and variable x defined in the function are different . One way to achieve is by returning value of x and collecting externally, which you may not be looking for.
Alternately, Since you have defined variable 'a' outside function ,it can be called global variable.
If you want to pass that to a function, and manipulate it, you need to define that variable ('a' in your case) inside the function as global. Something like below.
def inc(x):
global a
a = x+1
Now when the new value assigned to 'a' after 'x+1', it is retained after execution of 'inc(x)'
>>> a = 0
>>> inc(a)
>>> a
1
EDIT -1
As per comments by #DYZ . Its correct. declaring global a inside inc() function will always increment a.
A better alternative will be , in that case, to return x inside inc() and assign that value to any external variable.
Not an elegant solution, but works as intended.
def inc(x):
return x+1
Result
>>> a
0
>>> a = inc(a)
>>> a
1
>>> a = inc(a)
>>> a
2
>>> b = 0
>>> b = inc(b)
>>> b
1
>>> a
2
>>>
one can use yield to get variable values.
def inc(x,y,z):
x += 1
y+=1
z+=1
yield x,y,z #inc doesn't stop
yield x+y+z
a=b=c=0
gen=inc(a,b,c)
gen=list(gen)
a,b,c,sum=gen[0]+(gen[1],) #however, index must still be known
print a,b,c,sum

Confused with the python results after using some function [duplicate]

This question already has answers here:
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
Closed 5 years ago.
I have some problem here. Please look at my code and I don't know what's wrong in the code. It is simple but the results are confusing me. I attached the code below.
import numpy as np
import matplotlib.pyplot as plt
def S(xc):
N=len(xc)
r=0.0
s=0.0
# calculation quartile
for m in range(0,N-1):
for n in range(m+1,N):
if (xc[m] > xc[n]):
q=xc[m]
xc[m]=xc[n]
xc[n]=q
if (N % 4 < 2):
q=(xc[N-N/4-1] + xc[N-N/4])*0.5-(xc[N/4-1]+xc[N/4])*0.5
else:
q=xc[N-N/4-1]-xc[N/4]
#calculation standard deviation
for m in range(0,N):
r+=xc[m]
s+=xc[m]*xc[m]
r=np.sqrt(s/N-(r/N)*(r/N))
#calculation
if (q<r):
s=q
else:
s=r
hasil=0.9*(s/1.34)*pow(N,-0.2)
return hasil
fc=0.3
fm=0.02
mu=1
Nsim=10
bb=[]
for nn in range(0,Nsim):
bb.append((1+(mu*np.cos(2*np.pi*fm*nn)))*np.cos(2*np.pi*fc*nn))
print bb
print S(bb)
print bb
The code works while I just delete the function of "S" in the main function, however, after the "S" function, the data on variable "bb" was different even though I just print the same variable. I don't understand what happened.
I appreciate your help. Thanks a lot
Alvin
Calling S(bb) changes the contents of bb through statements like xc[m]=xc[n].
This is the (un)expected behaviour when working with mutable types in Python. In your case, passing in a mutable type bb, which is a list, to the function S.
You pass bb into S() with the variable name xc and within S, you've got the lines xc[m]=xc[n] and xc[n]=q. This changes the list xc, which is the same list bb points to.
Simpler example of what is happening:
>>> def funny(some_list):
... for i in range(len(some_list)): # terrible way to iterate over a list
... some_list[i] = some_list[i] * 2 # assigning back to the list
...
>>>
>>> alist = [1, 2, 3]
>>>
>>> funny(alist)
>>> alist
[2, 4, 6]
>>>
alist got changed after calling funny() because of the assignment to the list which I passed in.
If you don't want that behaviour, either pass in a copy of the list as S(bb[:]) or create a new list inside S by creating a copy of xc.
bb is a list, and lists are mutable.
Even though S() refers to its argument by a different name, xc, ultimately it refers to the same object. When S() executes statements such as xc[n]=q, the underlying list object is altered.

Categories

Resources