python objects : digging deeper - python

Hi all I know what this code does:
1.] My first problem
x = 4
y = x
But what about this. Why same addresses even for this case?
x = 4
y = 4
id(x)
12345678
id(y)
12345678
2.] My second problem
x = 42
y = x
x = x + 1
print x # Prints 43
print y # Prints 42
x = [1, 2, 3]
y = x
x[0] = 4
print x # Prints [4, 2, 3]
print y # Prints [4, 2, 3]
But why is it that in the case of list, both x & y got mutated together by the command x[0] = 4.
What is so different with lists, in such a behavior?
What makes them behave like this?
And most importantly what is the benefit of such a behavior?
why cant list, variables, tuples have all the properties of each other?

Small integers are cached in CPython, that's why their id's are same.
Integers are immutable, so modifying(i.e assigning it to a new object) one will not affect the other references.
Lists are mutable, so modifying any reference(in-place modification) to a mutable object will affect other references as well.
Small strings can have same id's too:
Integer example:
>>> x = 100
>>> y = x
>>> id(x),id(y)
(165193204, 165193204)
>>> x += 1
>>> id(x), id(y) #`x` now points to a new object, `y` still points to the old object
(165193192, 165193204)

Your first problem can be answered with memory optimization. If you dig a little further, for example:
x = 4
y = 4
# Both (may) have the same id
x += 1
# id of x and y will now be different
The second is reference semantics. Since just about everything in Python is really a reference, setting x = y is simply saying point y to the same thing that x points at. If you actually want a completely separate copy, you'll need to say so:
x = [1, 2, 3, 4]
y = x[:]
x[0] = 5
y
>>> [1, 2, 3, 4]

Related

Memory allocation of python object

I have the following code:
a = 2
b = a
a = a + 2
print (a)
print (b)
My question is why does b print out as 2 and not 4? If you assign a to b, doesn't b always reference to the memory of a?
Thanks
Because you have reassigned x to a new location by x = x + 1, and it's not about immutability.
For checking:
x = 2
y = x
>>> id(x), id(y)
(26726784, 26726784) # same
Then, change this:
x = x+1
>>> id(x), id(y)
(26726760, 26726784) # not same, because x reassigned to a new reference location.
If you do the same for list, which are mutable, you'll have the same result:
x=[5]
y=x
>>> id(x), id(y)
(139890260976056, 139890260976056)
on assignment:
x = x + [5]
>>> id(x), id(y)
(139890260094344, 139890260976056) # not same, id reallocated on assignment
>>> x, y
([5, 5], [5])
You'll see mutable behavior on list by using x.append(5), where both x and y change as you're modifying the object itself. But in this case, it's not the mutability of the object that are causing the difference. It's the assignment x=something else that changed the reference.
An interesting property about int in python is that smaller ones are pre-allocated. For instance, if you do:
x = 5
y = x
x = 5
id(x) == id(y) # True
The id will be the same, however, if you do:
x = 5000000000000
y = x
x = 5000000000000 # same as previous.
id(x) == id(y) # False
This is due to small integers having pre-allocated location while large ones don't. As such reassignment for large integer will find a different location instead.
This validates #juanpa.arrivillaga's point that this is due to assignment, not immutability.
The variable gets re-allocated to a new address when you re-assign a:
>>> a = 2
>>> b = a
# get memory address for a and b
>>> id(a)
4357961072
>>> id(b)
4357961072
# they are the same
# now reassign
>>> a = a + 2
# id of a has changed
>>> id(a)
4357961136
# id of b has not
>>> id(b)
4357961072
Not all python objects are mutable, here's list of which are.

How to add new value to a list without using 'append()' and then store the value in a newly created list?

I have been trying this a lot.
>>> x = [4,5]
>>> y = x.append(7)
>>> print y
None
>>>print x
[4, 5, 7]
How is this possible?
When I trying storing the values in the new list y and the print it, it results in None and it also changes the current list `x.
Is there any other way to do this in Python?
Because the function append() modifies the list and returns None.
One of the best practices to do what you want to do is by using + operator.
Let's take your example :
>>> x = [4, 5]
>>> y = x + [7]
>>> x
[4, 5]
>>> y
[4, 5, 7]
The + operator creates a new list and leaves the original list unchanged.
This is possible because x.append() is a method of list x that mutates the list in-place. There is no need for a return value as all the method needs to do is perform a side effect. Therefore, it returns None, which you assign your variable y.
I think you want to either create a copy of x and append to that:
y = x[:]
y.append(7)
or assign y the result of a list operation that actually creates a new list:
y = x + [7]
You can do
x = [4,5]
y = x + [7]
# x = [4, 5]
# y = [4, 5, 7]
The y is None because the append() method doesn't return the modified list (which from your code you are expecting).
So you can either split the statement,
y = x.append(7)
into
x.append(7)
y = x
OR use,
y = x + [7]
The second statement is more cleaner and creates a new list from x.
Just a note: Updating y won't make any updates in x as in the first statement it does. If you want to avoid this use copy.copy
Using Extend,
x = [3,4]
y =[]
y.extend(x+[7])
print(x, y)
produces output :
[3, 4] [3, 4, 7]
[Program finished]

Python: Why is this the output when executed?

Can someone tell me why, after I run the code below, these are the values the variables refer to now:
x = [1, 2, 3, 5]
x1 = [1, 2, 3, 5]
x2 = [1, 2, 3]
y = [1, 2, 3]
y1 = [1, 2, 3]
y2 = [1, 2, 3]
Why isn't y = [1, 2, 3, 5] ?
And why isn't x1 = [1, 2, 3]?
Because, following this logic:
a = 1
b = a
a = 3
b is still equal to 1, not 3.
Code is below. Thanks for your help.
def my_function(x, y):
x.append(5)
y = y + [5]
x = [1, 2, 3]
x1 = x
x2 = x[:]
y = [1, 2, 3]
y1 = y
y2 = y[:]
my_function(x, y)
The statement:
x1 = x
creates a copy of reference to the same list as pointed to by x. Now you are passing x to the function, where you append a value to x:
x.append(5)
this will affect the list pointed to by the original x outside the function, and eventually also reflect the modification for x1. Hence x1 is [1, 2, 3, 5].
Now, in case of y1, the only difference is this line in the function:
y = y + [5]
this line doesn't changes the original list referenced by y outside the function. It creates a local variable y, and initializes it to [1, 2, 3, 5]. But it doesn't changes the the original list referenced by y outside the function, and hence does not changes the list for y1. So, y1 and y are still [1, 2, 3].
You can verify it by checking the id of both the list inside the function, and the one outside:
>>> def my_function(x, y):
... x.append(5)
... y = y + [5]
... return x, y
...
>>>
>>> x = [1, 2, 3]
>>> x1 = x
>>> y = [1, 2, 3]
>>> y1 = y
>>>
>>> x2, y2 = my_function(x, y)
>>>
>>> id(x2) == id(x)
True
>>> id(y2) == id(y)
False
So, it's clear that id of original x is same as the one returned from the function. But the id of original y is not the same as returned y.
Simple. Mutable objects (such as lists) pass by reference, whereas immutable (such as integers) pass by value.
When you do x1 = x, x1 refers to x; so any change to x also applies to x1, because they refer to the same object. x.append(5) changes the original x, and thus x1 by reference.
However, when you do x2 = x[:], you are copying the contents of the list by slicing it. Thus, you are making a new list.
When you do y = y + [5], you are creating a new list in the function's scope. Thus, the global y (and y1 by reference) remains unchanged. y2 is just a copy of the original y, so the function does not affect it at all, either.
You didn't actually change the reference of y you just set it to a different reference. This wouldn't actually change the y you passed in. Just the y in the local scope of my_function.
Step through it here
You're actually dealing with a question of scope here. At first it looks like you're having aliasing issues, but your redefinition of the variables passed in only lasts within the function. Once the function is done running, the arguments passed in (to put it simply) go back to their original values. It is only within the function that they continue to have the values you reassign them to. Try printing the odd outputs before the end of the function.

How to append an element of a sublist in python

I have a list of lists in the form:
list = [[3, 1], [3, 2], [3, 3]]
And I want to split it into two lists, one with the x values of each sublist and one with the y values of each sublist.
I currently have this:
x = y = []
for sublist in list:
x.append(sublist[0])
y.append(sublist[1])
But that returns this, and I don't know why:
x = [3, 1, 3, 2, 3, 3]
y = [3, 1, 3, 2, 3, 3]
By doing x = y = [] you are creating x and y and referencing them to the same list, hence the erroneous output. (The Object IDs are same below)
>>> x = y = []
>>> id(x)
43842656
>>> id(y)
43842656
If you fix that, you get the correct result.
>>> x = []
>>> y = []
>>> for sublist in lst:
x.append(sublist[0])
y.append(sublist[1])
>>> x
[3, 3, 3]
>>> y
[1, 2, 3]
Although, this could be made pretty easier by doing.
x,y = zip(*lst)
P.S. - Please don't use list as a variable name, it shadows the builtin.
When you say x = y = [], you're causing x and y to be a reference to the same list. So when you edit one, you edit the other. There is a good explanation here about how references work.
Therefore you can use your code if you say instead
x = []; y = []
You also might want to try zip:
lst = [[3, 1], [3, 2], [3, 3]]
x,y = zip(*lst)
And as Sukrit says, don't use list as a variable name (or int or str or what have you) because though it is a Python built-in (load up an interpreter and type help(list) - the fact that something pops up means Python has pre-defined list to mean something) Python will cheerfully let you redefine (a.k.a. shadow) it. Which can break your code later.
That's because when you construct x and y like x = y = [] then x and y point to the same list. If you want to make them different lists, you should declare them like
x = []
y = []
You are setting x = y = [] meaning x is pointing to y which is pointing to the list so whenever you append to one, you are appending to the other. Change the code to something like this:
x = []
y = []
for sublist in list:
x.append(sublist[0])
y.append(sublist[1])
You can do:
x = [i[0] for i in list]
y = [i[1] for i in list]

Python references

Can someone explain why the example with integers results in different values for x and y and the example with the list results in x and y being the same object?
x = 42
y = x
x = x + 1
print x # 43
print y # 42
x = [ 1, 2, 3 ]
y = x
x[0] = 4
print x # [4, 2, 3]
print y # [4, 2, 3]
x is y # True
The best explanation I ever read is here:
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
Because integers are immutable, while list are mutable. You can see from the syntax. In x = x + 1 you are actually assigning a new value to x (it is alone on the LHS). In x[0] = 4, you're calling the index operator on the list and giving it a parameter - it's actually equivalent to x.__setitem__(0, 4), which is obviously changing the original object, not creating a new one.
If you do y = x, y and x are the reference to the same object. But integers are immutable and when you do x + 1, the new integer is created:
>>> x = 1
>>> id(x)
135720760
>>> x += 1
>>> id(x)
135720748
>>> x -= 1
>>> id(x)
135720760
When you have a mutable object (e.g. list, classes defined by yourself), x is changed whenever y is changed, because they point to a single object.
That's because when you have a list or a tuple in python you create a reference to an object.
When you say that y = x you reference to the same object with y as x does.
So when you edit the object of x y changes with it.
As the previous answers said the code you wrote assigns the same object to different names such aliases.
If you want to assign a copy of the original list to the new variable (object actually)
use this solution:
>>> x=[1,2,3]
>>> y=x[:] #this makes a new list
>>> x
[1, 2, 3]
>>> y
[1, 2, 3]
>>> x[0]=4
>>> x
[4, 2, 3]
>>> y
[1, 2, 3]

Categories

Resources