This question already has an answer here:
Understanding mutability in Python [closed]
(1 answer)
Closed 9 years ago.
My question is more of an understanding question than a strict programming question.
I know that python variables are actually pointers, which means they don't actually store a value, but rather point to the place in memory where that value is stored.
What i can't figure out is how the following 2 cases differ:
>>> a = 3
>>> b = a
>>> a
3
>>> b
3
>>>b = 4
>>> b
4
>>> a
3
The new value assigned to 'b' does not change the value pointed to by 'a'. As oppose to:
>>> a = [1,2,3]
>>> b = a
>>> a
[1,2,3]
>>> b
[1,2,3]
>>> b.append(4)
>>> b
[1,2,3,4]
>>> a
[1,2,3,4]
The new value assigned to b changed the value pointed to by a
Calling b.append doesn't assign b to a new list. It still points to the same position in memory.
>>> b = [1,2,3]
>>> id(b)
36586568L
>>> b.append(4)
>>> id(b)
36586568L
Since it is the underlying data that changes, any other identifiers also pointing to that data will be affected.
This has been covered many times before. Short answer is that they aren't strictly pointers. They are more like labels. In your top case, b is re-labeled to 4 and thus changes. In your bottom case (with the array) b is not re-labeled, but only it's contents appended to. That's why arrays seem to act differently.
Python "variables" ("binding" would be a more appropriate term) are not pointers, they are key->value pairs in a namespace. How the namespace and lookup are implemented is, well, an implementation detail, but you can consider it's a hash table (Python's dict).
The new value assigned to b changed the value pointed to by a
Where do you see the assignement operator in b.append(4) ? You're not rebinding b, you are mutating it. It's still the same list object that is bound to both names a and b.
Related
This question already has answers here:
Variable assignment and modification (in python) [duplicate]
(6 answers)
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 6 months ago.
Simple console sequence:
>>> A=[]; B=[]
>>> A.append(1)
>>> A
[1]
>>> A=[]
>>> A
[]
>>> B.append(A)
>>> B
[[]]
so far so expected.
B ist now a list containing one element, which is an empty list.
A is an empty list.
Let's continue:
>>> A.append(1)
>>> A
[1]
>>> A=[]
>>> A
[]
A has the same value as first time. Now the surprise (to me at least):
>>> B.append(A)
>>> B
[[1], []]
>>>
Why ????
How and where ist the previous content of A, the 1, stored, and why is [1] pre(!!)pended to B instead of another empty list being appended ?
You've stumbled on pointers! What's happening is that, each time you declare a new list using =[], a new object is created in memory. Each variable is only "pointing to" that location in memory.
So the sequence of what happened is that you created a list at location x01 and A was pointing at location x01. Then, you appended 1 to that list, so location x01 held an element "1". Then, you created a new list B at x02, which now holds a reference pointing to x01.
When you then overwrote A, you created a new list at x03 and set A to point there. However, B is still pointing at x02, which is still pointing at x01, which still holds a 1! Therefore, when you now append A to B, B is pointing at a location in memory (x02) which has two pointers: one at x01 and one at x03. x01 holds a 1 and x03 is empty.
The bottom line is, be cognizant of when you are creating a new object and when you are editing an existing object - editing an object will not create a new memory location, but creating a new object will. You can get some pretty unintuitive behavior with this if you're not careful.
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 4 years ago.
When I ran this script (Python v2.6):
a = [1,2]
b = a
a.append(3)
print a
>>>> [1,2,3]
print b
>>>> [1,2,3]
I expected print b to output [1,2]. Why did b get changed when all I did was change a? Is b permanently tied to a? If so, can I make them independent? How?
Memory management in Python involves a private heap memory location containing all Python objects and data structures.
Python's runtime only deals in references to objects (which all live in the heap): what goes on Python's stack are always references to values that live elsewhere.
>>> a = [1, 2]
>>> b = a
>>> a.append(3)
Here we can clearly see that the variable b is bound to the same object as a.
You can use the is operator to tests if two objects are physically the same, that means if they have the same address in memory. This can also be tested also using the id() function.
>>> a is b
>>> True
>>> id(a) == id(b)
>>> True
So, in this case, you must explicitly ask for a copy.
Once you've done that, there will be no more connection between the two distinct list objects.
>>> b = list(a)
>>> a is b
>>> False
Objects in Python are stored by reference—you aren't assigning the value of a to b, but a pointer to the object that a is pointing to.
To emulate assignation by value, you can make a copy like so:
import copy
b = copy.copy(a)
# now the code works as "expected"
Be aware this has performance disadvantages.
In the case of an array, there's a special method that relies on slices:
b = a[:]
# code also works as expected here
Update– In addition to this, with some objects you can use the constructor—this includes lists:
b = list(a)
Short answer - Pointers.
When you type b = a it is setting b to look at the same array that a looks at. You have to make a new array with copies of the elements to separate them. In this case, something like b = [n for n in a] would work fine. For more complex operations you may want to check out http://docs.python.org/library/copy.html.
You might want to look at this link. The problem you have here is a and b both point to the same memory location, so changing one changes the other. Instead, you want to do something like this:
a = [1,2]
b = list(a)
a is a pointer to the list [1,2].
When you do the assignment b = a the value of b is the address of the list [1,2].
So when you do a.append(3) you are not actually changing a, you are changing the list that a points to. Since a and b both point to the same list, they both appear to change when you modify the other.
If you simply want to copy the contents of list a to b, instead of making b a pointer to a:
b = a[:]
Using the slice operator will copy the contents of the list into b such that you example would become:
a = [1,2]
b = a[:]
a.append(3)
print a
>>>> [1,2,3]
print b
>>>> [1,2]
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 4 years ago.
When I ran this script (Python v2.6):
a = [1,2]
b = a
a.append(3)
print a
>>>> [1,2,3]
print b
>>>> [1,2,3]
I expected print b to output [1,2]. Why did b get changed when all I did was change a? Is b permanently tied to a? If so, can I make them independent? How?
Memory management in Python involves a private heap memory location containing all Python objects and data structures.
Python's runtime only deals in references to objects (which all live in the heap): what goes on Python's stack are always references to values that live elsewhere.
>>> a = [1, 2]
>>> b = a
>>> a.append(3)
Here we can clearly see that the variable b is bound to the same object as a.
You can use the is operator to tests if two objects are physically the same, that means if they have the same address in memory. This can also be tested also using the id() function.
>>> a is b
>>> True
>>> id(a) == id(b)
>>> True
So, in this case, you must explicitly ask for a copy.
Once you've done that, there will be no more connection between the two distinct list objects.
>>> b = list(a)
>>> a is b
>>> False
Objects in Python are stored by reference—you aren't assigning the value of a to b, but a pointer to the object that a is pointing to.
To emulate assignation by value, you can make a copy like so:
import copy
b = copy.copy(a)
# now the code works as "expected"
Be aware this has performance disadvantages.
In the case of an array, there's a special method that relies on slices:
b = a[:]
# code also works as expected here
Update– In addition to this, with some objects you can use the constructor—this includes lists:
b = list(a)
Short answer - Pointers.
When you type b = a it is setting b to look at the same array that a looks at. You have to make a new array with copies of the elements to separate them. In this case, something like b = [n for n in a] would work fine. For more complex operations you may want to check out http://docs.python.org/library/copy.html.
You might want to look at this link. The problem you have here is a and b both point to the same memory location, so changing one changes the other. Instead, you want to do something like this:
a = [1,2]
b = list(a)
a is a pointer to the list [1,2].
When you do the assignment b = a the value of b is the address of the list [1,2].
So when you do a.append(3) you are not actually changing a, you are changing the list that a points to. Since a and b both point to the same list, they both appear to change when you modify the other.
If you simply want to copy the contents of list a to b, instead of making b a pointer to a:
b = a[:]
Using the slice operator will copy the contents of the list into b such that you example would become:
a = [1,2]
b = a[:]
a.append(3)
print a
>>>> [1,2,3]
print b
>>>> [1,2]
This question already has answers here:
How are tuples unpacked in for loops?
(8 answers)
Closed 9 years ago.
I'm new to python and trying to work my way through http://yuji.wordpress.com/2011/06/22/python-imaplib-imap-example-with-gmail/ which has the following line:
result, data = mail.uid('search', None, "ALL") # search and return uids instead
Could someone explain this line?
Thank you.
It means that the function you have called returns an iterable, and the index 0 of the iterable is assigned to x and the index 1 is assigned to y. This is called tuple unpacking.
Eg)
>>> def func(a,b):
... return b,a
...
>>> a = 5
>>> b = 7
>>> a,b = func(a,b)
>>> a
7
>>> b
5
>>> x = func(a,b)
>>> x
(5, 7)
Edit to show that returning multiple values, they are packed as tuple by default and then unpacked at the other end. Since there is only one variable x here, the tuple is assigned to x.
Simple function for swapping two variables(Just for an example) that answers your question
At least, as of python 2.7.x, the function will unpack a tuple of 2 arguments returned from a function. If it returns anything other than 2 arguments in the tuple, I believe it will throw an error if you try to unpack more than this. If it returns 3 arguments and you unpack 2, for example, you will get an exception.
For example:
def func(a):
return (a,a+1,a*2)
a,b,c = func(7)
print a,b
==> 7 8 # NOTE Values
a = func(3)
print a
==> (3, 4, 6) # NOTE: TUPLE
a,b = func(9)
print a,b
==> Exception - ValueError: too many values to unpack
This may be different in 3.0+.
The other answer, that "the function you have called returns an iterable" is a good one. That is what is happening in your specific example. This is what is called "unpacking" in python. The following are examples of unpacking and assignment related to your question:
>>> a,b = 1,2
>>> a
1
>>> b
2
>>> a,b,c = ['do', 're', 'mi']
>>> a
'do'
>>> b
're'
>>> c
'mi'
>>>
This is one of the pretty features of Python syntax. If I am not mistaken, it is also optimized - i.e. the fastest way to achieve the result.
This question already has answers here:
Assigning values to variables in a list using a loop
(5 answers)
Closed 4 years ago.
I want to do the following:
a = 1
b = 2
c = 3
tom = [a,b,c]
for i in tom:
i = 6
The desired result is a = 6
The actual result is a = 1
I'm guessing that there is no way to do this without some kind of exec. Correct?
Initially I misunderstood your question, and I see that kindall got it right. But I think this question shows a need for a more detailed explanation of how Python works. The best way to think about variables in Python is to think of variable names as having arrows attached to them, and values as objects to which those arrows point. Variable names point to objects. So when you do this:
a = 1
You're really saying "a points to 1". And when you do this:
b = a
You're really saying "b points to the same object as a". Under normal circumstances, a variable can't point to another variable name; it can only point at the same object that the other variable points at. So when you do this:
tom = [a, b, c]
You aren't creating a list that points to the variable names a, b, and c; you're creating a list that points to the same objects as a, b, and c. If you change where a points, it has no effect on where tom[0] points. If you change where tom[0] points, it has no effect on where a points.
Now, as others have pointed out, you can programmatically alter the values of variable names, ether using exec as you suggested (not recommended), or by altering globals() (also not recommended). But most of the time, it's just not worth it.
If you really want to do this, my suggestion would be either simply to use a dictionary (as suggested by DzinX) or, for a solution that's closer to the spirit of your question, and still reasonably clean, you could simply use a mutable object. Then you could use getattr and setattr to programmatically alter the attributes of that object like so:
>>> class Foo():
... pass
...
>>> f = Foo()
>>> f.a = 1
>>> setattr(f, 'b', 2)
>>> getattr(f, 'a')
1
>>> f.b
2
Generally, the best solution is to just use a dictionary. But occasionally situations might arise in which the above is better.
tom = [a, b, c] puts the values 1, 2, and 3 into the list. Once these values are in the list, there's no way to know what name(s) are pointing to them. Assuming they are global variables, you could (but almost certainly shouldn't) do this:
tom = ["a", "b", "c"]
for n in tom:
globals()[n] = 1
Trying to set individual variables in a loop is almost always the wrong approach. The values clearly have something in common, otherwise you wouldn't want to change them all in a loop, so store them in a list (or a dictionary, if you need names for them) and access and change them there, instead of using individual variables.
More concisely,
a, b, c = map(lambda x: 6, [1, 2, 3])
or
a, b, c = 1, 2, 3
a, b, c = map(lambda x: 6, [a, b, c])
which could easily be generalised if you want to assign each to different values based on their original values.
It would be best if you didn't use variables, but keys in the dictionary, like this:
values = {
'a': 1,
'b': 2,
'c': 3
}
for k in values:
values[k] = 6
print values['a']
# prints: 6
If you want to change only some values, use:
for k in ['a', 'c']:
values[k] = 6
Here's an idea.
seniority = [range(1, 6)]
people = ["Richard", "Rob", "Steve", "Terry", "Micah"]
people = seniority
print people
output: [[1, 2, 3, 4, 5,]]