Reputation: 125
I am trying to append a list to another list inside a python function. Both lists are passed into the function as parameters.
For example, in below code, I was using res.append(cur)
but res
will return empty in this case. Instead I have to use res.append(cur[:])
to make a deep copy (this is actually a shallow copy corrected in comments). What is weird is that res.append(cur)
will work had both lists are not passed into the function but instead defined in the function.
I understand cur[:]
does a deep copy (this is actually a shallow copy corrected in comments) in this case. Does it mean res.append(cur)
will not append cur
because it's not a deep copy?
UPDATE: I have updated the full code below, basically it tries to print a subset of a given list of numbers, both res
and cur
are list defined outside helper()
, so if cur[:]
returns a shallow copy and it works, why does res.append(cur)
actually do in this case that does not work?
from __future__ import print_function
def subsetsWithDup():
set_list = [1,2,2]
res = []
cur = []
set_list.sort()
index = 0
helper(set_list, index, res, cur)
# below for loop prints nothing if res.append(cur) is used in helper() instead of res.append(cur[:])
for r in res:
print(r)
def helper(set_list, index, res, cur):
if index == len(set_list):
if cur not in res:
res.append(cur) # -> if I change this to res.append(cur[:]) this works
return
helper(set_list, index + 1, res, cur)
cur.append(set_list[index])
helper(set_list, index + 1, res, cur)
cur.pop()
if __name__ == "__main__":
subsetsWithDup()
Upvotes: 0
Views: 796
Reputation: 77892
Your problem is actually not that res.append(cur)
"doesn't work" - it of course works as expected -, but with how it works.
Python's "variables" are not symbolic names for memory locations, they are just name=>object pairs, and the same object can be referenced by many names at once. If you mutate an object thru one of it's name, you'll see the effect thru all the other names it's bound to, because all those names point to the same object:
>>> a = list("abc")
>>> a
['a', 'b', 'c']
>>> b = a
>>> c = b
>>> a is b
True
>>> a is c
True
>>> b.append("d")
>>> c
['a', 'b', 'c', 'd']
>>> a
['a', 'b', 'c', 'd']
>>> b
['a', 'b', 'c', 'd']
>>> c.pop()
'd'
>>> a
['a', 'b', 'c']
>>> b
['a', 'b', 'c']
>>> c
['a', 'b', 'c']
>>>
And this works the same for any attribute, collection etc, including of course lists of lists:
>>> lst = [a]
>>> lst[0]
['a', 'b', 'c']
>>> lst[0]
['a', 'b', 'c']
>>> c.append(42)
>>> lst
[['a', 'b', 'c', 42]]
Also, Python never implicitely copies anything - when you pass an object to a function, the function gets the object, not a copy of it:
>>> def foo(lst):
... print("in foo, lst = {} (id : {})".format(lst, id(lst)))
... lst.append([42])
...
>>> id(a)
140278039812000
>>> foo(a)
in foo, lst = ['a', 'b', 'c', 42] (id : 140278039812000)
>>> a
['a', 'b', 'c', 42, [42]]
So in your helper
function, you have this:
if index == len(set_list):
if cur not in res:
res.append(cur) # -> if I change this to res.append(cur[:]) this works
return
helper(set_list, index + 1, res, cur)
cur.append(set_list[index]) # HERE
helper(set_list, index + 1, res, cur)
cur.pop() # AND HERE !!!!
Notice the cur.append(xxx)
and cur.pop()
calls ? Guess what ? Yes, they mutate the cur
list. The one you appended to res
. Which is why you end up with an empty result. It's rather obvious if you trace code execution:
def helper(set_list, index, res, cur):
print("helper with index %s, res %s and cur %s" % (index, res, cur))
if index == len(set_list):
if cur not in res:
print("append %s to %s" % (cur, res))
res.append(cur) # -> if I change this to res.append(cur[:]) this works
return
print("first recursive call")
helper(set_list, index + 1, res, cur)
print("now res is %s and cur is %s" % (res, cur))
print("appending to cur")
cur.append(set_list[index])
print("now cur is %s" % cur)
print("second recursive call")
helper(set_list, index + 1, res, cur)
print("now res is %s and cur is %s" % (res, cur))
print("poping from cur")
cur.pop()
print("now res is %s and cur is %s" % (res, cur))
which produces:
helper with index 0, res [] and cur []
first recursive call
helper with index 1, res [] and cur []
first recursive call
helper with index 2, res [] and cur []
first recursive call
helper with index 3, res [] and cur []
append [] to []
now res is [[]] and cur is []
appending to cur
now cur is [2]
second recursive call
helper with index 3, res [[2]] and cur [2]
now res is [[2]] and cur is [2]
poping from cur
now res is [[]] and cur is []
now res is [[]] and cur is []
appending to cur
now cur is [2]
second recursive call
helper with index 2, res [[2]] and cur [2]
first recursive call
helper with index 3, res [[2]] and cur [2]
now res is [[2]] and cur is [2]
appending to cur
now cur is [2, 2]
second recursive call
helper with index 3, res [[2, 2]] and cur [2, 2]
now res is [[2, 2]] and cur is [2, 2]
poping from cur
now res is [[2]] and cur is [2]
now res is [[2]] and cur is [2]
poping from cur
now res is [[]] and cur is []
now res is [[]] and cur is []
appending to cur
now cur is [1]
second recursive call
helper with index 1, res [[1]] and cur [1]
first recursive call
helper with index 2, res [[1]] and cur [1]
first recursive call
helper with index 3, res [[1]] and cur [1]
now res is [[1]] and cur is [1]
appending to cur
now cur is [1, 2]
second recursive call
helper with index 3, res [[1, 2]] and cur [1, 2]
now res is [[1, 2]] and cur is [1, 2]
poping from cur
now res is [[1]] and cur is [1]
now res is [[1]] and cur is [1]
appending to cur
now cur is [1, 2]
second recursive call
helper with index 2, res [[1, 2]] and cur [1, 2]
first recursive call
helper with index 3, res [[1, 2]] and cur [1, 2]
now res is [[1, 2]] and cur is [1, 2]
appending to cur
now cur is [1, 2, 2]
second recursive call
helper with index 3, res [[1, 2, 2]] and cur [1, 2, 2]
now res is [[1, 2, 2]] and cur is [1, 2, 2]
poping from cur
now res is [[1, 2]] and cur is [1, 2]
now res is [[1, 2]] and cur is [1, 2]
poping from cur
now res is [[1]] and cur is [1]
now res is [[1]] and cur is [1]
poping from cur
now res is [[]] and cur is []
[]
And which is why appending a shallow copy of res
instead solves the issue - it's a different object, so it's not affected by the cur.append()
and cur.pop()
calls.
FWIW, there's an exellent articles about this whole topic, which you certainly want to read.
Upvotes: 1
Reputation: 3079
I do not see any problem while appending a list to another in a function.
As I said in my comment, we need more context to highlight the behavior...
def append(l1, l2):
l1.append(l2)
def extend(l1, l2):
l1.extend(l2)
def main():
append_l = ['a', 'b']
extend_l = ['c', 'd']
new = [1, 2, 3]
print("Append")
print(append_l)
append(append_l, new)
print(append_l)
print("Extend")
print(extend_l)
extend(extend_l, new)
print(extend_l)
if __name__ == "__main__":
main()
Append
['a', 'b']
['a', 'b', [1, 2, 3]]
Extend
['c', 'd']
['c', 'd', 1, 2, 3]
Upvotes: 0