Equal strings auto updating (List object reference) - python

I recently did an exam for university and I got asked what would be the output of this program:
def fun(x):
y=x
x.append(4)
print(str(x)+" "+str(y))
fun(["one","two",3,5.0])
I answered that the y list would be ["one","two", 3,5.0] and after appending 4 to it, the x list would be equal to the same but with a 4 at the end of it. To my surprise, when I printed both lists, they were equal even though the x list update was performed after establishing an equality between both lists. Why did this happen?
Thank you

You have given reference if list x to y. So any change in list x would also affect list y.
y=x
For example:
>>> x = ["one","two",3,5.0]
>>> y = x
>>> x[3] = 4
>>> x
['one', 'two', 3, 4]
>>> y
['one', 'two', 3, 4]
Here both x and y have same identity.
>>> x is y
True
>>> id(x)
3073118540L
>>> id(y)
3073118540L
You can better understand this using swampy module:
>>> from swampy.Lumpy import Lumpy
>>> lump = Lumpy()
>>> x = ["one","two",3,5.0]
>>> y = x
>>> x[3] = 4
>>> lump.object_diagram()
What you were expecting can be achieved by copying the list x to list y like this:
>>> x = ["one","two",3,5.0]
>>> y = x[:]
>>> x.pop()
5.0
>>> x
['one', 'two', 3]
>>> y
['one', 'two', 3, 5.0]
So by copying the content from x to y, they don't hold the same identity:
>>> id(x)
3073240428L
>>> id(y)
3073240588L
>>> x is y
False
Using swampy:
>>> from swampy.Lumpy import Lumpy
>>> lump = Lumpy()
>>> x = ["one","two",3,5.0]
>>> y = x[:]
>>> lump.draw_object()
>>> lump.object_diagram()
For better explanation visit here How do I copy an object in Python?

Actually x and y are labels that reference to object so when you assign y=x you crate 2 reference to one object , so when you change one of them you change the main object .
Also you may note that x , y are local variables when you made inplace changes like append you changed the main object , but if you use assignment python create a new object :
>>> def fun(x):
... y=x
... x=x+[3]
... print(str(x)+" "+str(y))
...
>>> fun(["one","two",3,5.0])
['one', 'two', 3, 5.0, 3] ['one', 'two', 3, 5.0]
in-place changes to objects do not classify names as locals; only actual name
assignments do. For instance, if the name L is assigned to a list at the top level of a
module, a statement L = X within a function will classify L as a local, but L.append(X)
will not. In the latter case, we are changing the list object that L references, not L itself—
L is found in the global scope as usual, and Python happily modifies it without requiring
a global (or nonlocal ) declaration. As usual, it helps to keep the distinction between
names and objects clear: changing an object is not an assignment to a name.(from learning python by mark lutz)
class A:
global L
L=[1,2]
def b(self):
L=[0,0]
return L
def c(self):
L.append(5)
return L
a=A()
print a.b()
print a.c()
result :
[0, 0]
[1, 2, 5]

Because list are mutable objects. see Python Data Model
In [1]: a = [1]
In [3]: b = a
In [4]: b
Out[4]: [1]
In [5]: b.append(2)
In [6]: a
Out[6]: [1, 2]
In [7]: b
Out[7]: [1, 2]
In [8]: id(a), id(b)
Out[8]: (140260765233376, 140260765233376)

Because the name y is bound to the same list as x
y = x
This is a great drawing of how that looks:
x y
| /
| /
["one", "two", 3, 5.0]
x.append(4)
x y
| /
| /
["one", "two", 3, 5.0, 4]

You can try following example. It will help you to get differences between assignment operator and method like copy(shallow), deepcopy.
>>> import copy
>>> l1 = [1,2, [1,2]]
>>> l1
[1, 2, [1, 2]]
#Create l2, l3, l4 by copy, deepcopy method and normal assignment.
>>> l2 = copy.copy(l1)
>>> l3 = copy.deepcopy(l1)
>>> l4 = l1
>>> l2
[1, 2, [1, 2]]
>>> l3
[1, 2, [1, 2]]
>>> l4
>>> [1, 2, [1, 2]]
#-----------------------Now Append value to l1
>>> l1.append(9)
>>> l1
[1, 2, [1, 2], 9]
>>> l2
[1, 2, [1, 2]]
>>> l3
[1, 2, [1, 2]]
>>> l4
>>> [1, 2, [1, 2], 9]
#-----------------------Now Append value to l1[2]
>>> l1[2].append(5)
>>> l1
[1, 2, [1, 2, 5], 9]
>>> l2
[1, 2, [1, 2, 5]]
>>> l3
[1, 2, [1, 2]]
>>> l4
>>> [1, 2, [1, 2, 5], 9]
#------------------------

Related

What's the difference between list.append(a[:]) and list.append(a) in python?

This is related to leetcode problem #39.
I started with results.append(solution), which does not append properly to the list, and found in the solution that results.append(solution[:]) works. What's the difference between these two syntax?
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
results=[]
def backtracking(candidates,target,start,solution,results):
#print(start,target,solution,results)
if target<0:
return
if target==0:
results.append(solution[:])
return
for i in range(start,len(candidates)):
solution.append(candidates[i])
backtracking(candidates,target-candidates[i],i,solution,results)
solution.pop()
backtracking(candidates,target,0,[],results)
return results
a[:] will create a new list.
c.append(b) appends the list b to c.
Following code will help understand this better -
>>> a=[1,2,3]
>>> b=[4,5]
>>> c=[1,2,3]
>>> a.append(b) #append b
>>> c.append(b[:]) #create new list and append
>>> a
[1, 2, 3, [4, 5]]
>>> c
[1, 2, 3, [4, 5]]
>>> b
[4, 5]
>>> a[3][0]=99 #modify a
>>> a
[1, 2, 3, [99, 5]] #a modified
>>> b
[99, 5] #so does b
>>> c
[1, 2, 3, [4, 5]] #modify c
>>> c[3][1]=99
>>> c #c modified
[1, 2, 3, [4, 99]]
>>> b #original b did not get modified
[99, 5]
>>>
As you can see from the id of the objects, making a slice creates a new list
>>> a = [1, 2, 3]
>>> id(a)
2711680383816
>>> id(a[:])
2711683338696
whereas assigning the list directly refers to the same object
>>> b = a
>>> id(b)
2711680383816
a is a list and a[:] is a new list with all elements copied.
>>> a = [1, 2, 3]
>>> a == a[:]
True
>>> a is a[:]
False
Let's have another list b = ["a", "b"]. append adds whatever you give it to the end of a list. If you append another list, the reference to that list gets added and might lead to un expected behaviours:
>>> b.append(a)
>>> b
["a", "b", [1, 2, 3]]
>>> a[0] = "c"
>>> b
["a", "b", ["c", 2, 3]]
>>> b[2][1] = 42
>>> a
["c", 42, 3]
>>> a is b[2]
True
You can see, that after appending a, if you change an element in a, it also changes in b. This is because b only has a reference to a. To prevent that, you can instead do b.append(a[:]). This will copy the values in a, so when you then change values in a, values in b stay what they were when you copied them:
>>> b.append(a)
>>> b
["a", "b", [1, 2, 3]]
>>> a[0] = "c"
>>> b
["a", "b", [1, 2, 3]]
>>> b[2][1] = 42
>>> a
["c", 2, 3]
>>> a is b[2]
False
So in your question, using solution[:] makes sure that whatever has been added to the results doesn't change when solution.append happens on the next iteration of the for loop.

Embedding Object reference to other objects, shallow/deepcopy python 3+

As explained in the python docs for V3.7 (https://docs.python.org/3/library/copy.html)
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
I tried to create a compound object as below by passing reference to a list to another list and as per the book I am reading, the new list L must store a reference to X and if X changes L must change too.
X = [1, 2, 3]
L = ['a', X, 'b']
However in my test run on IDLE, I can see that L contains the object [1,2,3] , hence does not store a reference to X, I tested this by changing X, which does not affect L:
>>> X = [1, 2, 3]
>>> L = ['a', X, 'b']
>>> X
[1, 2, 3]
>>> X=[1,2]
>>> L
['a', [1, 2, 3], 'b']
>>> X
[1, 2]
So my question is:
Does in python 3+ meaning of embedding reference to objects changed. If yes then does that mean shallow and deep copying does not differentiate any more, meaning they both make deep copies of the objects, as my tests show below and seems contrary to the documentation.
>>> L.copy()
['a', [1, 2, 3], 'b']
>>> import copy
>>> copy.deepcopy(L)
['a', [1, 2, 3], 'b']
>>>
X=[1,2] rebinds the name X to a new object. Modifying the original object works as expected:
>>> import copy
>>> X=[1,2,3]
>>> L = [1, X, 2]
>>> L
[1, [1, 2, 3], 2]
>>> L_ = copy.copy(L)
>>> L_
[1, [1, 2, 3], 2]
>>> X.append('WOW') # modify here
>>> L, L_
([1, [1, 2, 3, 'WOW'], 2], [1, [1, 2, 3, 'WOW'], 2]) # the change is reflected in both objects

Create alias of part of a list in python

Is there a way to get an alias for a part of a list in python?
Specifically, I want the equivalent of this to happen:
>>> l=[1,2,3,4,5]
>>> a=l
>>> l[0]=10
>>> a
[10, 2, 3, 4, 5]
But what I get is this:
>>> l=[1,2,3,4,5]
>>> a=l[0:2]
>>> l[0]=10
>>> a
[1, 2]
If numpy is an option:
import numpy as np
l = np.array(l)
a = l[:2]
l[0] = 10
print(l)
print(a)
Output:
[10 2 3 4 5]
[10 2]
slicing with basic indexing returns a view object with numpy so any change are reflected in the view object
Or use a memoryview with an array.array:
from array import array
l = memoryview(array("l", [1, 2, 3, 4,5]))
a = l[:2]
l[0]= 10
print(l.tolist())
print(a.tolist())
Output:
[10, 2, 3, 4, 5]
[10, 2]
You could embed each element into its own mutable data structure (like a list).
>>> l=[1,2,3,4,5]
>>> l = [[item] for item in l]
>>> l
[[1], [2], [3], [4], [5]]
>>> a = l[:2]
>>> a
[[1], [2]]
>>> l[0][0] = 10
>>> l
[[10], [2], [3], [4], [5]]
>>> a
[[10], [2]]
However, I recommend trying to come up with a solution to your original issue (whatever that was) that doesn't create issues of its own.
What you're looking for is a view of the original list, so that any modifications are reflected in the original list. This is doable with the array in the numpy package:
>>> import numpy
>>> x = numpy.array([1, 2, 3])
>>> y = x[2:]
>>> y[0] = 999
>>> x
array([ 1, 2, 999])

Python programming problems with class and lists [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 7 years ago.
I was coding with Python and a bug made me notice this.
Say, I have this code:
class test1(object):
def __init__(self):
self.hello = ["hello"]
self.text_back = test2().stuff(self.hello)
print "With list: ",self.hello[0], self.text_back[0]
class test2(object):
def stuff(self,text1):
self.asdf = text1
self.asdf[0] = "goodbye"
return self.asdf
a = test1()
#---------------
class test3(object):
def __init__(self):
self.hello = "hello"
self.text_back = test4().stuff(self.hello)
print "Without list:",self.hello, self.text_back
class test4(object):
def stuff(self,text1):
self.asdf = text1
self.asdf = "goodbye"
return self.asdf
b = test3()
The output is:
With list: goodbye goodbye
Without list: hello goodbye
I have the identical code between the two, except that one is list and one isn't. Why am I getting different results?
In code, we are not creating new variable for list, we just create reference variable name.
Demo:
>> l1 = ["hello"]
>>> l1= l1
>>> l2= l1
>>> id(l1), id(l2)
(3071912844L, 3071912844L)
>>> l1
['hello']
>>> l2
['hello']
>>> l2[0] = "goodbye"
>>> l2
['goodbye']
>>> l1
['goodbye']
>>>
Normally copying a list:
>>> l1 = [1,2,3]
>>> l2 = l1[:]
>>> id(l1), id(l2)
(3071912556L, 3071912972L)
>>> l1.append(4)
>>> l1
[1, 2, 3, 4]
>>> l2
[1, 2, 3]
>>>
Nested datastructures:
>>> l1 = [1,2, [1,2], [3,4]]
>>> l2 = l1[:]
>>> l1.append(4)
>>> l1
[1, 2, [1, 2], [3, 4], 4]
>>> l2
[1, 2, [1, 2], [3, 4]]
>>> l1[2].append(3)
>>> l1
[1, 2, [1, 2, 3], [3, 4], 4]
>>> l2
[1, 2, [1, 2, 3], [3, 4]]
>>>
Deep copies in Python
>>> import copy
>>> l1 = [1,2, [1,2], [3,4]]
>>> l2 = copy.deepcopy(l1)
>>> l1.append(4)
>>> l1[2].append(3)
>>> l1
[1, 2, [1, 2, 3], [3, 4], 4]
>>> l2
[1, 2, [1, 2], [3, 4]]
>>>
Link

python list index is the same list [duplicate]

This question already has answers here:
Confusing [...] List in Python: What is it?
(9 answers)
Closed 9 years ago.
I just seen a output like below - just want to know what is happening here.
>>> l = [1,2,3,4]
>>> l[0]=l
>>> l
[[...], 2, 3, 4]
Why the l[0] value has displayed like this? Can anyone explain me why this behavior.
I was thinking it'd return like, [[1,2,3,4], 2, 3, 4].
Cheers,
Kalai
It shows the ... because otherwise it would have to infinitely recurse.
A list object in Python is a pointer to a list- assigning it like l[0] = l doesn't make a copy. For instance, try
l1 = [1, 2, 3, 4]
l2 = [1, 2]
l2[0] = l1
print l2
# [[1, 2, 3, 4], 2]
l2[0].append(5)
print l1
# [1, 2, 3, 4, 5]
Notice that even though you never changed l1 explicitly, it has now been appended to.
Therefore, when you place a list within itself, that item of the list is still a link to the entire list. After your code above, try doing:
l[1] # ==> 2
l[0][1] # ==> 2
l[0][0][1] # ==> 2
Use a copy of the list to avoid infinite recursion:
In [10]: l = [1,2,3,4]
In [11]: l[0] = l[:]
In [12]: l
Out[12]: [[1, 2, 3, 4], 2, 3, 4]
If you would have used a PrettyPrinter, the output would had been self explanatory
>>> l = [1,2,3,4]
>>> l[0]=l
>>> l
[[...], 2, 3, 4]
>>> pp = pprint.PrettyPrinter(indent = 4)
>>> pp.pprint(l)
[<Recursion on list with id=70327632>, 2, 3, 4]
>>> id(l)
70327632

Categories

Resources