Python: Why is this the output when executed? - python

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.

Related

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 objects : digging deeper

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]

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]

"backtracking" variables in python del function

The del statement in python, when applied to a list, does something very strange. It not only deletes the entry in the list in question, but also "backtracks" to delete the same element in whatever list the variable was derived from.
For instance:
>>> x
[1, 2, 3, 4, 5]
>>> z=x
>>> del z[1]
>>> z
[1, 3, 4, 5]
>>> x
[1, 3, 4, 5]
In other words, even though I apply delete to z, the deletion is also applied to x. This "backtracking" doesn't come up with any other commands or functions, e.g.
>>> x=[1,2,3,4,5]
>>> z=x
>>> z+[6]
[1, 2, 3, 4, 5, 6]
>>> x
[1, 2, 3, 4, 5]
applying the add function to z leaves x unaffected, as it very well should.
This is causing me all sorts of headaches with the delete 'NA' scrip that I'm working on. Do you know what's going on with the del statement, and how can I get around this problem so that if I set z=x and then apply del to z, x remains unchanged?
z = x does not make a copy of the list. z and x now point to the same list. Any modification of that list will be visible through both names, z and x.
With z + [6], you did not assign the result back to z. Try printing z after doing that. It will be unchanged, just like x.
z += [6] would be necessary to assign the result back to z and it would have the same behavior as your first example.
You can copy the list using one of these methods:
z = x[:]
z = list(x)

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