Global variables in python, undesired behavior. - python

here i am updating x and if I print y after I updated x, y will show the reflection. However, in the first example y doesn't show that change. This is where I get lost at. Can someone explain this to me?
x = 2
y = x + 3
### printing y will yield 5
x = 8 # ok am I updating the same storage space that x pointed to, or is abandoning it and creating
# another with a value of 8?
### printing y will still yield 5 (why is that? if Y always points to x and adds 3 to it?)
in contrast with this:
x = [1,2,3]
y = [x,4,5,6]
x[0] = 50
y[2] = 80
z = y[:] + [100,101,1020

x = 2 A new object with the value 2 is created, x refers to it
y = x + 3 A new object with the value 5 is created, y refers to it
x = 8 A new object with the value 8 is created, x now refers to it. The reference count on the object with value 2 is decremented, and is a candidate for garbage collection.
x = [1,2,3] A new list ("object of type/class list") is created, and x refers to it.
y = [x,4,5,6] A new list-object is created, and y refers to it. The previous object x referred to is basically gone. It's so-called "garbage" and may be collected by the so-called "garbage collector".
x[0] = 50 Changes the first element of the list referenced by x to 50
y[2] = 80 Changes the third element of the list referenced by y to 80
z = y[:] A new list-object is created, which contains a copy of the elements refered to by y. This doesn't copy the reference (x) in the first element. See copy.deepcopy() for that.

You wrote:
printing y will still yield 5 (why is that? if Y always points to x and adds 3 to it?)
Your misunderstanding starts here. y does not point to x. yhas the value 5. It almost sounds like you are trying to equate this with the model of a spreadsheet where cells can have formulas. Coding is not like that; when you assign something to y, you are assigning a value, not a formula.

When you use the = operator with a variable name as the left hand value, this binds (or re-binds) the name on the left side to the object on the right side. y = x + 3 makes y refer to an int object with the value 5, and then x = 8 makes the variable x refer to an int object with the value 8.
The the left hand side is something like x[0], you're doing item assignment, and only changing the 0th item in x; the name x still refers to the original list, which is also a member of the list pointed to by y.

Think of it as a set of boxes. X is the name of a box, as is y, when we are talking about single variables rather than arrays.
So x = 2 means put a 2 in the box labelled x.
y = x + 3 means take what's in box x, add 3 to it and put it in box y.
x = 8 means replace what's in box x with 8.
y has already been worked out so can't be changed.
With arrays like x = [1, 2, 3] x doesn't refer to a box, it refers to a collection of boxes.
Its like x refers to box 1, 2 and 3, which each happen to have the values 1 2 & 3 in them.
So if I change the contents of the first box in the list x indicates, x itself is unchanged, it still is the list of boxes - of which one of them has had it's value changed.
y in this case is a list of boxes, of which the first one is not really a box, but contains a pointer to another set of boxes.

Let's take your example, and replace the variables:
Original:
x = 2
y = x + 3
### printing y will yield 5
x = 8 # x now holds the value of 8 instead of 2
### printing y will still yield 5 (why is that? if Y always points to x and adds 3 to it?)
in contrast with this:
x = [1,2,3]
y = [x,4,5,6]
x[0] = 50
Replaced:
x = 2
y = 2 + 3
### printing y will yield 5
x = 8
### printing y will still yield 5 because it is set to 2+3
x = [1,2,3]
y = [[1,2,3],4,5,6] #[1,2,3] is the same list as x
x[0] = 50 # x still holds the value of the same list,
#however you're changing the value of the first element of the,
#you're not changing x itself though.
In the first example, you're changing the value of x itself. In the second you're changing a property of x, while leaving x as the same value.

If you really want that behaviour, you have to use a function:
>>> x = 2
>>> y = lambda: x + 3
>>> y()
5
>>> x = 8
>>> y()
11
because x is immutable, so assigning will always create new objects that will not reflect future changes.

y = x + 3
The value for y gets immediately computed. That's all.

Related

Multi-assignment in Python [duplicate]

This question already has answers here:
Python assigning multiple variables to same value? list behavior
(13 answers)
Closed 4 months ago.
When initializing multiple variables in Python is seems that every variable is different (i.e. not a reference) :
>>>x = y = z = 6
>>>x=5
>>>y=0
>>>print(x,y,z)
>>>5 0 6
but when I am using a numpy array every variable is actually a reference:
import numpy as np
x = y = z = 6*np.ones(1)
x[0] = 5
y[0] = 0
print(x,y,z)
[0.] [0.] [0.]
Why am I seeing this difference?
This is down to a difference in types. Integers are not passed by reference, so each variable gets its own copy of the number. If you look at a different object like a list though:
x = y = z = [0,1,2]
x[0] = 2
print(x, y, x)
[2, 1, 2], [2, 1, 2], [2, 1, 2]
this is also passed by reference, so they all change together.
Strings and integers are the main examples I can think of that you the value, while everything else (I think) is a reference.
Interesting aside related to this: in place operations behave differently for strings and integers vs other objects. For example:
x = 1
print(id(x))
x += 1
print(id(x))
behaves differently to
y = []
print(id(y))
y += [1]
print(id(y))
You can see in the first instance a new object is now assigned to the x variable, while in the second the y variable is the same object.
You need to make copies:
a = 6*np.ones(1)
x, y, z = (a.copy() for _ in range(3))
# or directly
# x, y, z = (6*np.ones(1) for _ in range(3))
x[0] = 1
(x, y, z)
output:
(array([1.]), array([6.]), array([6.]))

Why Updating value from 2 lists behave differently? (one of the item in list is a single-item list)

x = [1,3,6,[18]]
y = list(x)
print(y)
x[3][0] = 15
x[1] = 12
print(x)
print(y)
In the above code, updating x[3][0] in x is reflected in y (which is the list of x), but the update in x[1] = 12 is not reflected in y
Why this is not getting updated in y?
A picture worth thousand words. Here is the picture that is missing:
(let's pretend forgetting all *terminology for a moment now)
Credits to all posts. Labor (of the wording & picturing) is mine.
First thing first, when you try to use list constructor to create y: list(x) or [:] it just produces a shallow copy (ie. the outermost container is duplicated, but the copy is filled with reference to the same item (as [18] shown in picture)
So later if you update x's [18] it will update the reference point - that's why both lists see the change of 15. But when you update other object, it directly change the integer object in the container x but not in y.
Lesson learned - Copies are shallow by Default! in Python.
# See different code example:
x = [1,3,6,[18]]
y = x[:] # new list, but it's shallow copy (last item)
print(x, id(x)) # different memory
print(y, id(y)) # but same content to start with
x[3][0] = 15 # assign new value - reflect in both x, y
x[1] = 12 # assign new value - only affect x
print(x, id(x))
print(y, id(y)) # last item got changed too; because sharing
https://i.imgur.com/3MJLTUE.png
What is happening is a shallow copy. When you wrote y=list(x), this created a reference from y to x, and not an actual copy. What you need is a deep copy, a copy that creates a new list(including nested lists) instead of referencing the old one. .copy() won't work, because nested lists are still references even though the rest of the copied list is a separate list.
Here is the code for a deep copy:
import copy
x=[1,3,6,[18]]
y=copy.deepcopy(x)
print(y)
x[3][0]=15
x[1]=12
print(x)
print(y)
By using list() you create a new reference (y) to the same addresses in memory that (x) is referring.
Element 1 in x and y is an integer which is immutable. Once you use x[1]=12 basically you create a new object in memory for number 12 and x[1] will refer to that object. However, y[1] still refers to previous object (i.e. 3).
Element 3 in in x and y is a list which is mutable. Once, you use x[3][0], it updates the same object in memory. So, both x and y reflect the change.
I hope I could convey the concept.
They do not behave differently. Changes to x are never reflected in y.
x = [1,3,6,[18]]
y = list(x)
print(y) # [1,3,6,[18]]
x[3] = [15]
print(y) # [1,3,6,[18]]
However, changes to the 1-element list are reflected both in x and in y:
z = [18]
x = [1,3,6,z]
y = list(x)
print(y) # [1,3,6,[18]]
z[0] = 15
print(y) # [1,3,6,[15]]
The reason why integers and lists are different, the reason why you don't see changes to integers reflected in y is because it's possible to make changes to a list, but it's not possible to make changes to int:
x = [1,3,6,[18]]
y = list(x)
print(y) # [1,3,6,[18]]
3 = 12 # SyntaxError: cannot assign to literal here.
In python, every type is either mutable or immutable. Lists are mutable; integers are immutable.

How would I add something to a number over and over again in Python [duplicate]

This question already has answers here:
Program that asks user for numbers and outputs the sum and the amount of numbers entered
(4 answers)
Closed 6 months ago.
I am trying to add numbers in python. I know that it would be x + 3 when you are adding it, but when I try to do it with the program I am writing, it doesn't work out how I want it to be!
Here is the code:
x = 0
list = []
for i in range (1,100):
y = x + 3
list.append(y)
print (list)
and the output we get is :
[3,3,3,3,3,3...]
When I want it to be:
[3,6,9,12,15]
Can someone please tell me what I am doing wrong?
Thanks!
You need to assign the result back to x so that it accumulates the values. You keep adding to the original value of x, since x never changes.
x = 0
l = []
for i in range(1, 100):
x += 3
l.append(x)
print(l)
BTW, don't use list as the name of a variable. It's the name of a Python built-in function.
Your not increasing or incrementing the variable x. So after the statement list.append(y), in a new line, add x+=3
You don't need the variable x,
Change the line y=x+3 to y=i*3
In your script you are not redefining the value of x each time so you are doing 0+3 countless times. I is the index of the loop so will increase after each iteration, allowing you to have an increasing value in the list output
You can write it in one line like this:
[x for x in range(1, 100) if x % 3 == 0]
or:
[x*3 for x in range(1, 100)]
You can do this:
you are not updating the x so you keep adding the originalx value. so x have to be updated. but since you have y you can do this.
y = 0
mylist = []
for i in range (1,100):
y += 3
mylist.append(y)
print (mylist)
or do this you can just update the x value, then it works.
x = 0
mylist = []
for i in range (1,100):
y = x + 3
x += 3 #update the x variable
mylist.append(y)
print (mylist)
And don't use list as the name of a variable. It's the name of a Python built-in function. In python so use another word like mylist or something.
this will help
x = 0
lst = []
for i in range (1,100):
y = x+i*3
lst.append(y)
print(list)
x is always 0, so you keep running y = 0 + 3, then adding that to the list
You dont need a y variable at all, you can run this in your loop
x += 3
lst.append(x)
Or, maybe simpler
lst = [3*x for x in range(1, 100)]

Changing variable values in for loop?

I'm confused about how variables modified in part of a for loop behave outside the loop. Encountered this problem when doing a more complicated analysis but realised it's a basic for loop issue, easily shown with a toy example:
x, y, z = 1, 2, 3
for i in [x, y, z]:
i += 10
print(i)
# prints 11
# prints 12
# prints 13
print(x, y, z)
# prints 1 2 3
I would've expected the change to each iterated-over variable in the loop to stick outside of the loop, but evidently this isn't the case. I gather it's probably something about variable scope? Could someone explain how this works?
Actually you don't even need a for loop to showcase this what you call an issue but is actually not.
Take this simpler example:
x = 1
i = x
i += 10
print(x)
print(i)
Ofcourse x is not modified because it holds an immutable value of 1.
Here are immutable types in Python:
all primitive types: int, float, bool
plus tuple and str
Immutability means no shared reference.
So that when you have:
x = 1
y = 1
doesn't mean that x and y are referring to the same exact value 1 but instead each of the two variables have their "duplicate" instance of the same value. So that changing x doesn't affect y at all.
The same way, when you have:
x = 1
i = x
this is going to create a "brand new value of 1" and assign it to i, so that modifying i doesn't affect variable x at all.
But now, to get the behavior you want you can do something like:
x, y, z = 1, 2, 3
l = [x, y, z]
for i in range(len(l)):
l[i] += 10
x, y, z = l
Or if you really really want to be a little bit quirky, you can do:
x, y, z = 1, 2, 3
for var in locals().keys():
if var in ['x', 'y', 'z']:
locals()[var] += 10
But keep in mind that it is a good design decision from the creators of the language to keep certain types immutable, and so you should work with it instead and completely avoid using locals() as above or something else unnatural.
Actually it is not related to scopes.
Here you are iterating using the value i and increasing only i value, not any of x,y or z variable. So x,y and z remain unchnaged.
To change, use like this:
b = {'x': 1,'y': 2, ,'z': 3}
for m in b.keys():
b[m] += 10
The thing is that i variable is like a "temporary" variable in the for statement. So you are assigning i to a value of the array in each iteration. Look at the next example:
array = [1, 2, 3]
for i in array:
i += 10 #here we are modifying the "temporary" variable
print(array) #prints 1 2 3
index = 0
for i in array:
array[index] += 10 #here we are modifying the array
index += 1
print(array) #prints 11 12 13

What is the term to describe this property of how lists work?

I am looking for the proper term to describe this well-known property of collection objects, and more importantly, the way the stack diagram changes when variables are used to reference their elements:
>>> x = 5
>>> l = [x]
>>> x += 1
>>> l
[5]
>>> x
6
What is the name of what the list is doing to the variable x to prevent it from being bound to any changes to the original value of x? Shielding? Shared structure? List binding? Nothing is coming back from a Google search using those terms.
Here's an example with more detail (but doesn't have a definition, unfortunately).
Credit to ocw.mit.edu
What is the name of what the list is doing to the variable x to prevent it from being bound to any changes to the original value of x? Shielding? Shared structure? List binding? Nothing is coming back from a Google search using those terms.
Because the list isn't doing anything, and it isn't a property of collections.
In Python, variables are names.
>>> x = 5
This means: x shall be a name for the value 5.
>>> l = [x]
This means: l shall be a name for the value that results from taking the value that x names (5), and making a one-element list with that value ([5]).
>>> x += 1
x += 1 gets rewritten into x = x + 1 here, because integers are immutable. You cannot cause the value 5 to increase by 1, because then it would be 5 any more.
Thus, this means: x shall stop being a name for what it currently names, and start being a name for the value that results from the mathematical expression x + 1. I.e., 6.
That's how it happens with reference semantics. There is no reason to expect the contents of the list to change.
Now, let's look at what happens with value semantics, in a hypothetical language that looks just like Python but treats variables the same way they are treated in C.
>>> x = 5
This now means: x is a label for a chunk of memory that holds a representation of the number 5.
>>> l = [x]
This now means: l is a label for a chunk of memory that holds some list structure (possibly including some pointers and such), which will be initialized in some way so that it represents a list with 1 element, that has the value 5 (copied from the x variable). It cannot be made to contain x logically, since that is a separate variable and we have value semantics; so we store a copy.
>>> x += 1
This now means: increment the number in the x variable; now it is 6. The list is, again, unaffected.
Regardless of your semantics, you cannot affect the list contents this way. Expecting the list contents to change means being inconsistent in your interpretations. (This becomes more obvious if you rewrite the code to l = [5]; x = l[0]; x += 1.)
I would call it "immutability" of the contained object.
I think you compare your situation with the following one:
x = []
l = [x]
x += [1]
print l # --> [[1]]
The difference is:
In this situation (the mutable situation), you mutate your original object x which is contained in the list l.
In your situation however, you have x point to an immutable object (5), which is then added to the list. Afterwards, this reference is replaced by the one to 6, but only for x, not for the list.
So x += <something> either modifies x or replaces it with another object depending on the nature of the object type.
EDIT: It as well has nothing to do with the nature of lists. You can achieve the same with 2 variables:
x = 5
y = x
print x, y, x is y
x += 1
print x, y, x is y
vs.
x = []
y = x
print x, y, x is y
x += [1]
print x, y, x is y
The first one will change x due to the immutability of ints, resulting to x is y being false, while in the second one, x is y remains true because the object (the list) is mutated and the identity of the object referenced to by x and y stays the same.
The behavior you're describing has to do with references. If you're familiar with c, you're probably also familiar with "pointers." Pointers in c can get really complicated, and Python uses a data model that greatly simplifies things. But having a bit of background in c helps understand Python's behavior here, which is closely related to the behavior of c pointers. So "pointer," "reference," and "dereference" are all terms that are related to what you're talking about, although none of them is quite a "name" for it. Perhaps the best name for it is "indirection" -- though that's a bit too abstract and inclusive; this is a very specific kind of indirection. Perhaps "reference semantics"? Here's a slide from a talk by GvR himself that uses that term, and a google search turns up a few useful hits.
But if you don't have any background in c, here's my best explanation. In short, you can think of a Python name as a pointer to an object. So when you assign a name to a value, you're "pointing" that name at the value. Then, when you assign a new value to the name, you point it at a new value; but the old value isn't changed at all as a result. This seems natural when thinking about names as pointers here; the "value of the name" is changed, but the "value of the value" is not.
The thing that's a bit confusing is that += behaves inconsistently. When you use += on a number, the result makes perfect sense using the above metaphor:
x = 5
y = x
x += 1
print x, y
# 6 5
The behavior is exactly the same as it would be if you did x = x + 1.
But sometimes += is overloaded in a way that does in-place mutation. It's a pragmatic, but slightly inconsistent, idiom:
x = [5]
y = x
x += [6]
print x, y
# [5, 6] [5, 6]
So here, the "value of the value" is changed. This is actually not my favorite thing about Python, but there are good reasons for it.
The only thing list is doing to x in your example is reading it; it interacts with the variable in no other way. In fact the list generated by the expression [x] does not interact with the variable x at all.
If you had to introduce a piece of jargon for it, it would be value-capture, or perhaps simply independence.
I think the reason why there isn't a special term for this is that it is (a) not something that requires much discussion (b) is an aspect of strict by-value semantics where variables always hold references. Those kinds of semantics are pretty much the norm now (except where it is by reference with variables actually naming bits of memory containing objects). I think OP was expecting by-name or lazy semantics.
What you're effectively doing is constructing a list with one element that references the same object as x. Then you bind a new value to x while the list still references the old element. This is because += returns a reference to a new object (6) and leaves the old 5 object intact. Integers are immutable in Python.

Categories