Issue understanding modification of variable in jupyter notebook - python

I'm quite new to jupyter and just can't figure out how this work :
With these 3 blocks of code, a multiple execution of the 3rd one make n incremental, i.e. [1,2], then [1,3], [1,4], etc
n = [1,2]
--
def fonction(x):
y=x
y[1]=x[1]+1
return y
--
res = fonction(n)
print(res)
This is the minimal example i can provide. I don't understand why the variable n is modified though it's only the argument in the 3rd block (and the 2nd block isn't modifying the argument of the function !)
Thanks a lot for your patience explaining 101 jupyter to this old engineer, sincerely

Since you are writing y = x inside of fonction that is not giving y the value contained in x, but rather making y point to the value of x. In this case y is pointing to x and x is pointing to n (via input of fonction) therefore y[1] = will assign a value to the variable which is stored in the location that y is pointing to... Since y is pointing to the same location as n it changes that list. This means that when you reference n again it is still pointing to the same list, but the list has been modified via y[1] = x[1] + 1.
You can fix this issue by using y = x[:] which will create an entirely new list for y so that it does not point to the list stored in n.
This can be applied to your code like so:
def fonction(x):
y=x[:] #notice the change here
y[1]=x[1]+1
return y

Related

first list gets auto updated aftter appending another second list to it and then changing the second list in Python

please take a look at this code
a = [int(x) for x in bin(2)[2:]]
coordinates = []
sum = [1,3]
for x in a:
if x == 1:
coordinates.append(sum)
m = ((3*(sum[0]**2)+A) * libnum.invmod(2*sum[1],p)) % p
x3 = (m**2 - 2*sum[0]) % p
sum[1] = ((m*(sum[0]-x3) - sum[1]) + p) % p
sum[0] = x3
my sum list gets updated in the loop and the new values should be added to the coordinates list if they match the conditions. the problem is that after appending the sum list to the coordinates list whenever the values change in sum they also change in coordinates. there is some kind of link between them. Can you please help me with this problem? How can I unlink them so that just the values are appended and stay fixed.
This is because when you assign a list value to a new variable, it only stores the reference number, when you append this variable to a new list, the same reference number is being used. So when you later change the value, it changes the values of the list defined by that reference number, hence changing what that variable stores wherever it was used.
e.g
coordinates = []
some = [1,3] # avoid using 'sum' as variable name as it is a built in function
coordinates.append(some)
print("coordinates initially:", coordinates)
some.append("interesting")
print("coordinates after chaning 'some':", coordinates)
Output:
coordinates initially: [[1, 3]]
coordinates after chaning 'some': [[1, 3, 'interesting']]
This doenst make sense for small lists but when you consider that lists can hold huge amounts of values, it makes it much faster to use reference numbers
Thus, the solution is that we need to infact append a copy of the some list and not just the reference number.
For this, we can do the following:
import copy
# We can use the copy.deepcopy() function and pass it the list we need copied
coordinates.append(copy.deepcopy(some))

Same print statement provides different output for sorting algorithm (Insertion Sort)

I have coded up the algorithm for insertion sort and have ensured that it works; however, depending on how I print the sorted list, the output is different. For example,
x = some_unsorted_list
x = insertionSort(x)
print(x)
will print the sorted list just fine. On the other hand,
x = some_unsorted_list
insertionSort(x)
print(x)
will print some partially sorted-unsorted list. Here is my code for selection sort:
def insertionSort(L):
endList = len(L) - 1
for i in range(1,endList+1,1):
insertVal = L[i]
j = i - 1
while j >= 0 and L[j] > insertVal:
L[j+1] = L[j]
j -= 1
L[j+1] = insertVal
#print(L)
return L
Note that the print statement commented out always provides the sorted list, which is causing my confusion.
Edit (Important information I left out): Here's how I'm calling the insertion sort. Let v1, v2, and v3 be unsorted lists.
for i in [v1,v2,v3]:
x = list(i)
insertionSort(x)
print(x)
And recall, this is where errors arise. However the following code does not produce errors:
print(insertionSort(v1))
print(insertionSort(v2))
print(insertionSort(v3))
When you pass a variable to a function, a new copy is made. So, when you are passing x to your function, a new variable L will be made which will have the same value as x.
Now, we have 2 different variables named x and L.
In your function, you are making changes to L and outside the function, you are printing x. Therefore, you are getting wrong results. Because L was sorted NOT x.
The difference among codes
In your first code snippet x = insertionSort(x), overrides the value of x with the value returned by function (which will be a sorted list always). So, it prints the right result.
In your second code snippet, you are NOT overriding the value of x. Therefore, you are getting wrong results.

python local variable- when do I have to assign a value?

I'm an amateur programmer and would like to seek advice while learning codes. Here I encounter some issues.
I found that when I remove the comment "#X=3" and make it into a code from the below then the code works. Without X=3, the code results into UnboundLocalError.
Browsed online, it's related to global and local variable but I can't see how it's related. And when does X has to be denoted before the while loop? and why "for y in primes" doesn't need to pre-define "y"?
Main purpose of the code: count the # of prime numbers up to (num)
def count_primes2(num):
primes = [2]
#x = 3
if num < 2:
return 0
while x <= num:
for y in primes: # use the primes list!
if x%y == 0:
x += 2
break
else:
primes.append(x)
x += 2
print(primes)
return len(primes)
As per design pattern variable should be created just before to use. In the code you are using x without creating or initializing default value.
"y" = you are iterating the list (primes). So in each iteration y will be initialized by the current value.So it will not give any error.
To expand, since you are using x in the while loop criteria, yes, it has to be defined before. You don't need to define y before the for loop because the for y in primes line defines y as each item in the list.
A rough translation to plain English:
while x <= num: == As long as this number is less than or equal to this other number, do the following
for y in primes == do the following for each item, named y, in primes
hopefully that wasn't more confusing
You need to create (and assign a value to) a variable before you use it. If you try to use a variable's value before creating the variable, then you get the exception. You do exactly this in the while expression: You ask if its value is below or equal to num, but it does not even exist yet, it has no value, this raises the exception.
Now, why do you get the UnboundLocalError?
The compiler goes through your code before the code gets executed. In this compile step it notices that you somewhere in your function assign a value to X, namely in the line x += 2. (There are even two of them.) This marks the variable for the compiler as a local variable.
So if you try to access the variable before the assignment takes place, the variable doesn't exist yet, but the code already knows that is supposed to be a local variable, hence the UnboundLocalError.
Without any assignment statement to X in the function you would have gotten a NameError because during execution of the while statement the interpreter then searches for a global variable of this name.

How to change the value of a reference contained inside a list?

In python how can i change the value of references contained inside a list ?
For example in the following code
x = 'stack'
y = 'exchange'
l = [x,y]
l[1] = 'overflow'
The last line would actually replace y with 'overflow' but what I want to do is change the reference contained at l[1] to 'overflow' how can I achieve the same ?
PS : y = 'overflow' is not an accepted answer :P
The context probably will make the problem clearer there are 50 variable that I have inside a list initialised to zero and I want them to set their values at run-time.so that when at a later point of time I do a print variable50 , i get the new value not zero.
You do rebind reference at l[1] to 'overflow' with l[1] = 'overflow'. If you want to actually modify the string object, you can't. Strings are immutable.
"change the value of references" is awkward phrasing and there are two separate things that I can imagine that you mean:
1) cause the element of the list to refer to something else; but that's what your example does and is what you say you don't want.
2) cause the referred-to element of the list to change. The referred-to element is an object, so this is only possible by invoking code that changes the object's state. Python's str type is immutable, so no such code exists for the example situation.
What are you really trying to do? Idiomatic Python code embraces reference semantics rather than trying to fight them.
You want to add a new element to your list and sort it on position 1? That can be achieved with basic list functions.. Just a question why aren't you using dictionaries?
Is this interesting ?
x = 'stack'
y = 'exchange'
z = 'overflow'
l = [x, y]
l
['stack', 'exchange']
use python's swap
y, z = z, y
but then, you have to re-assign the list
l = [x, y]
l
['stack', 'overflow']

Creating a back-up list in python

I want to create a back-up list of another list in python. Here is an example of the code.
x = [1,2,3]
y = x
x.pop(0)
print y
This however yields the result y = [2,3] when I want it to yield [1,2,3]. How would I go about making the y list independent of the x list?
A common idiom for this is y = x[:]. This makes a shallow copy of x and stores it in y.
Note that if x contains references to objects, y will also contain references to the same objects. This may or may not be what you want. If it isn't, take a look at copy.deepcopy().
Here is one way to do it:
import copy
x = [1,2,3]
y = copy.deepcopy(x)
x.pop(0)
print x
print y
from the docs here
While aix has the most parsimonious answer here, for completeness you can also do this:
y = list(x)
This will force the creation of a new list, and makes it pretty clear what you're trying to do. I would probably do it that way myself. But be aware- it doesn't make a deep copy (so all the elements are the same references).
If you want to make sure NOTHING happens to y, you can make it a tuple- which will prevent deletion and addition of elements. If you want to do that:
y = tuple(x)
As a final alternative you can do this:
y = [a for a in x]
That's the list comprehension approach to copying (and great for doing basic transforms or filtering). So really, you have a lot of options.

Categories

Resources