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
Related
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.
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]
#------------------------
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
I'm a Python beginner and I'm currently going through Zed Shaw's course "Learn Python the Hardway"
So, in exercise 32 we are told:
How do you make a 2-dimensional (2D) list?
That's a list in a list like this: [[1,2,3],[4,5,6]]
I did this:
# Extra 1
global_list = [[1, 2, 3]]
inside_list = []
for i in global_list[0]:
inside_list.append(i)
global_list.append(inside_list)
print(global_list)
But I’m not entirely convinced that's the correct way. My question is: Is there a way to get the same result without ever leaving the for i in.... loop?
I also tried this, to no avail.
global_list = [[1, 2, 3]]
inside_list = []
for i in global_list[0]:
inside_list.append(i)
global_list.append(inside_list)
print(global_list)
Thanks in advance for your answers.
Lists can be appended and inserted into a list just like any other object, e.g:
outer_list = []
print(outer_list)
inner_list1 = [1, 2, 3]
outer_list.append(inner_list1)
print(outer_list)
inner_list2 = [4, 5, 6]
outer_list.append(inner_list2)
print(outer_list)
I am not sure if you already went over list comprehension. However, one nice way of doing what you are doing is:
>>> global_list = [[1,2,3]]
>>> global_list.append([i + 3 for i in global_list[0]])
>>> print global_list
[[1, 2, 4], [4, 5, 6]]
The question was "How do you make a 2-dimensional (2D) list?". The answer given was "That's a list in a list like this: [[1,2,3],[4,5,6]]". Literally, that's the answer:
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> print a
[[1, 2, 3], [4, 5, 6]]
You can also do this:
>>> a = [[1, 2, 3]]
>>> a.append([4, 5, 6])
>>> a
[[1, 2, 3], [4, 5, 6]]
You don't need a for loop to append a list inside another list:
>>> a = [[1, 2, 3]]
>>> a[0]
[1, 2, 3]
>>> a.append(a[0])
>>> a
[[1, 2, 3], [1, 2, 3]]
However, this makes the second element of the list of lists the same as the first, so if you change one you change the other:
>>> a[0] is a[1]
True
>>> a[0][0] = 4
>>> a
[[4, 2, 3], [4, 2, 3]]
What you can do to make a copy of the list is list(a[0]):
>>> a = [[1, 2, 3]]
>>> a[0]
[1, 2, 3]
>>> a[0] is a[0]
True
>>> list(a[0])
[1, 2, 3]
>>> a[0] is list(a[0])
False
>>> a.append(list(a[0]))
>>> a
[[1, 2, 3], [1, 2, 3]]
>>> a[0] is a[1]
False
>>> a[0][0] = 4
>>> a
[[4, 2, 3], [1, 2, 3]]
Is thery any nice compact way of adding an item to the last nested list of an outer list.
I.e:
a=[1,2,[3,4,[5,6]]]
and after the insertion of 7 i want my list to become
a=[1,2,[3,4,[5,6,7]]]
You can use indexing to reference the last inner list:
>>> a=[1,2,[3,4,[5,6]]]
>>> a[2][2].append(7)
>>> a
[1, 2, [3, 4, [5, 6, 7]]]
Or you can write a function to seek out the last inner list:
>>> def findlastlist(s):
while s and isinstance(s[-1], list):
s = s[-1]
return s
>>> a=[1,2,[3,4,[5,6]]]
>>> findlastlist(a).append(7)
>>> a
[1, 2, [3, 4, [5, 6, 7]]]
If you don't look for a general solution:
>>> a=[1,2,[3,4,[5,6]]]
>>> a[-1][-1].append(7)
>>> print a
[1, 2, [3, 4, [5, 6, 7]]]
If you do, here's a naive implementation (for usage, see the doctests):
A function, that returns the nesting level of a list:
def nesting(alist, level=0):
""" Get the nesting level of a list.
>>> nesting([])
0
>>> nesting([1, 2])
0
>>> nesting([1, [2]])
1
>>> nesting([1, 2, [3, 4, [5, 6]]])
2
>>> nesting([1, 2, [3, 4, [5, 6]], [33, 44, [55, 66]]])
2
"""
try:
alist[-1]
except IndexError:
return level
except TypeError:
return level - 1
else:
return nesting(alist[-1], level=level + 1)
A function, that appends an element to alist at a certain level:
def append_nested(alist, element, level):
"""
>>> x = []
>>> append_nested(x, 'hello', nesting(x))
['hello']
>>> x = [1, 2, 3]
>>> append_nested(x, 'hello', nesting(x))
[1, 2, 3, 'hello']
>>> x = [1, 2, 3, [4, 5]]
>>> append_nested(x, 'hello', nesting(x))
[1, 2, 3, [4, 5, 'hello']]
>>> x = [1, 2, 3, [4, 5], [7, 8]]
>>> append_nested(x, 'hello', nesting(x))
[1, 2, 3, [4, 5], [7, 8, 'hello']]
>>> x = [1,2,[3,4,[5,6]]]
>>> append_nested(x, 7, nesting(x))
[1, 2, [3, 4, [5, 6, 7]]]
>>> x = [1,2,[3,4,[5,6]]]
>>> append_nested(x, 7, 0) # append to the 'root' list
[1, 2, [3, 4, [5, 6]], 7]
"""
z = alist
for i in range(level):
z = z[-1]
z.append(element)
return alist
To test them, just run:
if __name__ == '__main__':
import doctest
doctest.testmod()
If you need a general solution try this:
def last_inner_append(x, y):
try:
if isinstance(x[-1], list):
last_inner_append(x[-1], y)
return x
except IndexError:
pass
x.append(y)
return x
>>> x = [1,2,[3,4,[5,6]]]
>>> y = 7
>>> last_inner_append(x, y)
[1,2,[3,4,[5,6,7]]]
It recursively works through the final element of nested lists until it reaches something that is not a list. At this point it sticks your value there. The try/except block allows it to handle empty lists.