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

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.

Related

Function that modifies an argument outside that function without using "return" python [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 3 years ago.
Let's take a simple code:
y = [1,2,3]
def plusOne(y):
for x in range(len(y)):
y[x] += 1
return y
print plusOne(y), y
a = 2
def plusOne2(a):
a += 1
return a
print plusOne2(a), a
Values of 'y' change but value 'a' stays the same. I have already learned that it's because one is mutable and the other is not. But how to change the code so that the function doesn't change the list?
For example to do something like that (in pseudocode for simplicity):
a = [1,2,3,...,n]
function doSomething(x):
do stuff with x
return x
b = doSomething(a)
if someOperation(a) > someOperation(b):
do stuff
EDIT: Sorry, but I have another question on nested lists. See this code:
def change(y):
yN = y[:]
for i in range(len(yN)):
if yN[i][0] == 1:
yN[i][0] = 0
else:
yN[i][0] = 1
return yN
data1 = [[1],[1],[0],[0]]
data2 = change(data1)
Here it doesn't work. Why? Again: how to avoid this problem? I understand why it is not working: yN = y[:] copies values of y to yN, but the values are also lists, so the operation would have to be doubled for every list in list. How to do this operation with nested lists?
Python variables contain pointers, or references, to objects. All values (even integers) are objects, and assignment changes the variable to point to a different object. It does not store a new value in the variable, it changes the variable to refer or point to a different object. For this reason many people say that Python doesn't have "variables," it has "names," and the = operation doesn't "assign a value to a variable," but rather "binds a name to an object."
In plusOne you are modifying (or "mutating") the contents of y but never change what y itself refers to. It stays pointing to the same list, the one you passed in to the function. The global variable y and the local variable y refer to the same list, so the changes are visible using either variable. Since you changed the contents of the object that was passed in, there is actually no reason to return y (in fact, returning None is what Python itself does for operations like this that modify a list "in place" -- values are returned by operations that create new objects rather than mutating existing ones).
In plusOne2 you are changing the local variable a to refer to a different integer object, 3. ("Binding the name a to the object 3.") The global variable a is not changed by this and continues to point to 2.
If you don't want to change a list passed in, make a copy of it and change that. Then your function should return the new list since it's one of those operations that creates a new object, and the new object will be lost if you don't return it. You can do this as the first line of the function: x = x[:] for example (as others have pointed out). Or, if it might be useful to have the function called either way, you can have the caller pass in x[:] if he wants a copy made.
Create a copy of the list. Using testList = inputList[:]. See the code
>>> def plusOne(y):
newY = y[:]
for x in range(len(newY)):
newY[x] += 1
return newY
>>> y = [1, 2, 3]
>>> print plusOne(y), y
[2, 3, 4] [1, 2, 3]
Or, you can create a new list in the function
>>> def plusOne(y):
newList = []
for elem in y:
newList.append(elem+1)
return newList
You can also use a comprehension as others have pointed out.
>>> def plusOne(y):
return [elem+1 for elem in y]
You can pass a copy of your list, using slice notation:
print plusOne(y[:]), y
Or the better way would be to create the copy of list in the function itself, so that the caller don't have to worry about the possible modification:
def plusOne(y):
y_copy = y[:]
and work on y_copy instead.
Or as pointed out by #abarnet in comments, you can modify the function to use list comprehension, which will create a new list altogether:
return [x + 1 for x in y]
Just create a new list with the values you want in it and return that instead.
def plus_one(sequence):
return [el + 1 for el in sequence]
As others have pointed out, you should use newlist = original[:] or newlist = list(original) to copy the list if you do not want to modify the original.
def plusOne(y):
y2 = list(y) # copy the list over, y2 = y[:] also works
for i, _ in enumerate(y2):
y2[i] += 1
return y2
However, you can acheive your desired output with a list comprehension
def plusOne(y):
return [i+1 for i in y]
This will iterate over the values in y and create a new list by adding one to each of them
To answer your edited question:
Copying nested data structures is called deep copying. To do this in Python, use deepcopy() within the copy module.
You can do that by make a function and call this function by map function ,
map function will call the add function and give it the value after that it will print the new value like that:
def add(x):
return x+x
print(list(map(add,[1,2,3])))
Or you can use (*range) function it is very easy to do that like that example :
print ([i+i for i in [*range (1,10)]])

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

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]

Python global vs. local variables — numbers and arrays treated differently [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 3 years ago.
Let's take a simple code:
y = [1,2,3]
def plusOne(y):
for x in range(len(y)):
y[x] += 1
return y
print plusOne(y), y
a = 2
def plusOne2(a):
a += 1
return a
print plusOne2(a), a
Values of 'y' change but value 'a' stays the same. I have already learned that it's because one is mutable and the other is not. But how to change the code so that the function doesn't change the list?
For example to do something like that (in pseudocode for simplicity):
a = [1,2,3,...,n]
function doSomething(x):
do stuff with x
return x
b = doSomething(a)
if someOperation(a) > someOperation(b):
do stuff
EDIT: Sorry, but I have another question on nested lists. See this code:
def change(y):
yN = y[:]
for i in range(len(yN)):
if yN[i][0] == 1:
yN[i][0] = 0
else:
yN[i][0] = 1
return yN
data1 = [[1],[1],[0],[0]]
data2 = change(data1)
Here it doesn't work. Why? Again: how to avoid this problem? I understand why it is not working: yN = y[:] copies values of y to yN, but the values are also lists, so the operation would have to be doubled for every list in list. How to do this operation with nested lists?
Python variables contain pointers, or references, to objects. All values (even integers) are objects, and assignment changes the variable to point to a different object. It does not store a new value in the variable, it changes the variable to refer or point to a different object. For this reason many people say that Python doesn't have "variables," it has "names," and the = operation doesn't "assign a value to a variable," but rather "binds a name to an object."
In plusOne you are modifying (or "mutating") the contents of y but never change what y itself refers to. It stays pointing to the same list, the one you passed in to the function. The global variable y and the local variable y refer to the same list, so the changes are visible using either variable. Since you changed the contents of the object that was passed in, there is actually no reason to return y (in fact, returning None is what Python itself does for operations like this that modify a list "in place" -- values are returned by operations that create new objects rather than mutating existing ones).
In plusOne2 you are changing the local variable a to refer to a different integer object, 3. ("Binding the name a to the object 3.") The global variable a is not changed by this and continues to point to 2.
If you don't want to change a list passed in, make a copy of it and change that. Then your function should return the new list since it's one of those operations that creates a new object, and the new object will be lost if you don't return it. You can do this as the first line of the function: x = x[:] for example (as others have pointed out). Or, if it might be useful to have the function called either way, you can have the caller pass in x[:] if he wants a copy made.
Create a copy of the list. Using testList = inputList[:]. See the code
>>> def plusOne(y):
newY = y[:]
for x in range(len(newY)):
newY[x] += 1
return newY
>>> y = [1, 2, 3]
>>> print plusOne(y), y
[2, 3, 4] [1, 2, 3]
Or, you can create a new list in the function
>>> def plusOne(y):
newList = []
for elem in y:
newList.append(elem+1)
return newList
You can also use a comprehension as others have pointed out.
>>> def plusOne(y):
return [elem+1 for elem in y]
You can pass a copy of your list, using slice notation:
print plusOne(y[:]), y
Or the better way would be to create the copy of list in the function itself, so that the caller don't have to worry about the possible modification:
def plusOne(y):
y_copy = y[:]
and work on y_copy instead.
Or as pointed out by #abarnet in comments, you can modify the function to use list comprehension, which will create a new list altogether:
return [x + 1 for x in y]
Just create a new list with the values you want in it and return that instead.
def plus_one(sequence):
return [el + 1 for el in sequence]
As others have pointed out, you should use newlist = original[:] or newlist = list(original) to copy the list if you do not want to modify the original.
def plusOne(y):
y2 = list(y) # copy the list over, y2 = y[:] also works
for i, _ in enumerate(y2):
y2[i] += 1
return y2
However, you can acheive your desired output with a list comprehension
def plusOne(y):
return [i+1 for i in y]
This will iterate over the values in y and create a new list by adding one to each of them
To answer your edited question:
Copying nested data structures is called deep copying. To do this in Python, use deepcopy() within the copy module.
You can do that by make a function and call this function by map function ,
map function will call the add function and give it the value after that it will print the new value like that:
def add(x):
return x+x
print(list(map(add,[1,2,3])))
Or you can use (*range) function it is very easy to do that like that example :
print ([i+i for i in [*range (1,10)]])

Function that works as append for numpy.array

How can I write a function that works like array.append() for numpy.array?
I have tried this
import numpy as np
def append_np(ar, el):
ar = np.append(ar, el)
z = np.array([5], dtype='int32')
z = np.append(z, 6)
append_np(z, 7)
print z
but this code appends only '6':
[5 6]
"that works like array.append()"
First of all, the data structure in Python you most likely are referring to here as "array" is called "list".
Then, the append() methods for Python lists and Numpy arrays behave fundamentally different. Say that l is a Python list. l.append() modifies the list in-place and returns None. In contrast, Numpy's append() method for arrays does not change the array it operates on. It returns a new array object.
See: http://docs.scipy.org/doc/numpy/reference/generated/numpy.append.html
A copy of arr with values appended to axis. Note that append does not
occur in-place: a new array is allocated and filled.
This explains why you need to return the result of your append_np() function and assign the return value, as in new_z = append_np(z, 7).
You have probably used this function for a Python list:
def append(ar, el):
ar = ar.append(el)
and called it like this:
z = [1, 2]
append(z, 7)
print z
And you have seen that it has modified your z, indeed. But why, what has happened in this function? The object that was passed as first argument (bound to the name ar) got modified in-place. That is why z "on the outside" changed. You made use of this side effect of the function without knowing, and this is dangerous. Within the function the name ar got re-assigned to the None singleton object (which is the return value of the list append method). You did not return this object or use it, so this assignment served no purpose in your program. You discovered yourself that this approach is problematic, because when you re-structured your function to append_np() you suddenly realized that it did not have a "side effect" on z.
That is, for Python lists you would not outsource the append operation into another function. You would just, from the very beginning, state:
z = [1, 2]
z.append(7)
print z

Categories

Resources