Appending an empty list with list in recursive function - python

def permute(nums):
result = []
get_permute([], nums, result)
return result
def get_permute(current, num, result):
if not num:
result.append(current+[])
for i, v in enumerate(num):
current.append(num[i])
get_permute(current, num[:i] + num[i + 1:], result)
current.pop()
if __name__ == "__main__":
r = permute([1,2,3])
for perm in r:
print(perm)
What does current + [] do in result.append(current+[]) if I remove +[] it printing blank lists.

It's making a copy of the list. When you remove it, you run into the List of lists changes reflected across sublists unexpectedly problem, because the outer list contains many references to the same list, instead of references to many different lists.
You should be able to replace it with current.copy() (using Python >= 3.3) or list(current) to avoid similar confusion among future readers. (There are a lot of ways to copy a list. Pick the one you like and stick with it.)

What does the + [] do?
It generates a new list with the same contents as the old list.
>>> x = [1]
>>> id(x) == id(x + [])
False
>>> x == x + []
True
Why do I need this?
Whitout adding copies to your result, you would have the same list many times in your result and everytime you update that list, it affects your result.
>>> x = [1, 2]
>>> result = []
>>> result.append(x)
>>> x.append(3)
>>> result.append(x)
>>> result
[[1, 2, 3], [1, 2, 3]]
Some possible ways to make the could more readable would be
result.append(current[:])
or
result.append(list(current))
Why does it return blank lists if you remove the + []?
Because if you do not append copies to the result, there would be just one list in the result but multiple times. And you call .append(num[i]) on this list just as often as .pop(), which results in that list be empty.

Related

Python: Inserting sublist into main list is replacing all the existing sublist

I have the below python code where an insert a sublist tmp[] into the main list lis[]. Every time, I add the sublist to the main list, all the list elements in the main list get replaced by the sublist
tmp = [0]
lis = []
tmp[0] = 0
lis.insert(0,tmp)
print lis
tmp[0] = 1
lis.insert(1,tmp)
print lis
Output:
[[0]]
[[1], [1]]
What change should I make to get output like below
[[0]]
[[0], [1]]
Another way of doing it:
lis = []
tmp = [0]
lis.insert(0,tmp)
print(lis)
tmp = [1]
lis.insert(1,tmp)
print(lis)
Output:
[[0]]
[[0], [1]]
The technical issue is that lists are mutable, which means that you are actually inserting the same object twice instead of inserting two distinct objects. Changes to tmp are reflected in both places that you see it, the 0th and 1st entries of lis. There isn't a great way to get around this without making a new variable or overwriting the old name.
>>> tmp = [0]
>>> lis = []
>>> tmp[0] = 0
>>> lis.insert(0,tmp)
>>> print(lis)
[[0]]
>>> tmp2 = [1]
>>> lis.insert(1,tmp2)
>>> print(lis)
[[0], [1]]
This behavior in Python occurs because lists in Python are mutable.
According to the Python documentation:
Mutable objects can change their value but keep their id().
So essentially when you insert the list tmp into lis at index 0, tmp is lis[0] evaluates to True and id(tmp) == id(lis[0]) also evaluates to True. Then when you later change the element at index 0 of the list tmp from 0 to 1, because mutable objects "change their value but keep their id()", both tmp is lis[0] and id(tmp) == id(lis[0]) still evaluate to True. So therefore inserting tmp[0] into lis[1] results in duplicate elements at lis[0] and lis[1].
Solution
To mitigate this instead of using lists we can use an immutable object (as you'd expect immutable objects change their value but lose their id()) such as tuples. But of course tuples have limitations too, because tuples are immutable we cannot modify their contents after they have been initialized (through insert, remove etc.). Tuples in Python are notated the same as lists except instead of initializing them with [] (or square brackets) we use ().
For example a solution (using tuples) to your example problem would be:
tmp = (0,) # In order to initialize a tuple a comma is always required after the first element
lis = []
lis.insert(0, tmp)
print(lis)
tmp = (1,) # We cannot modify tuples therefore we must create a new one.
lis.insert(1, tmp)
print(lis)

Why does a loop through a list of lists not recognise, that a list is a copy

I encountered a (in my opinion) extremely strange behavior, when looping through a list of lists. It is very difficult to explain, but here is an example code:
k = [[0],[1],[2]]
for lis in k:
lis_copy = lis
lis_copy.append(0)
print lis
When executing this, I get:
[0, 0]
[1, 0]
[2, 0]
This is very strange for me, as the list which is appended is a copy of lis,
but lis is appended as well. I would assume this list to be untouched.
For example doing the same with a list of integers the following happens:
k = [0,1,2]
for num in k:
num_copy = num
num_copy = 0
print num
Output:
0
1
2
Just as expected num is not touched by the manipulation of num_copy.
If somebody could explain why this is happening and how to avoid this,
like how to disconnect the lis_copy from is, that would be great.
Wow, I am amazed I did not encounter mayor problems before, without knowing this. I think I should review quiet some of my code. Anyway I thought this is somehow connected to the loop, this seems not to be the case, therefore I think the best explanation can be found here:
How to clone or copy a list?
This is because Python lists (and dicts) are not copied to a new list, but the new list becomes a reference to that list. if you truly want to copy the list, use deepcopy
You could use copy.copy() or copy.deepcopy()to avoid this behavior:
import copy
k = [[0],[1],[2]]
for lis in k:
lis_copy = copy.copy(lis)
lis_copy.append(0)
print lis
Output:
[0]
[1]
[2]
Source: https://docs.python.org/2/library/copy.html
Case a:
k = [[0],[1],[2]]
for lis in k:
lis_copy = lis
lis_copy.append(0)
print lis
We have a pointer to a list, and inside the loop we have another pointer made that points to the inner list objects. Then a zero is appended to each object.
Case b:
k = [0,1,2]
for num in k:
num_copy = num
num_copy = 0
print num
We have a pointer to a list, and inside the loop we have another pointer made that points to the inner integers. The difference is that in this case the pointer is changed to then point to a zero object rather than the list elements.

Having trouble with printing mutated variable outside function (Python) [duplicate]

This question already has answers here:
having trouble with lists in python
(5 answers)
Closed 7 years ago.
def satisfiesF(L):
"""
Assumes L is a list of strings
Assume function f is already defined for you and it maps a string to a Boolean
Mutates L such that it contains all of the strings, s, originally in L such
that f(s) returns True, and no other elements
Returns the length of L after mutation
"""
result = []
for l in L:
result.extend(l)
L = list(result)
for i in result:
if i != 'a':
L.remove(i)
return len(L)
Basically what I am trying to do is mutate the list, L within the function. From my testing, it appears that the end result of L is exactly what I am looking for it to be, but when I print L outside of the function it just spits out the original, unmutated list. Can anyone help me fix this issue?
disclaimer: This is a homework problem, but I'm not looking for a solution to the whole problem - just this one issue. Thanks!
You are changing the name L to point to a new list inside the function, in line -
L = list(result)
This does not change the list outside the function, because only the local variable L changed to point to a new list, its previous reference was not changed (And the name/variable outside the function, that was used to call this function still points to the old list).
You should iterate over copy of result (created using result[:]) and remove from result , without changing the reference L points to. And then at end use L[:] = result , to make changes to the list reference that L points to.
Example -
list1 = ['a','b','c','d','e','f']
def satisfiesF(L):
result = []
for l in L:
result.extend(l)
for i in result[:]:
if i != 'a':
result.remove(i)
L[:] = result
return len(L)
satisfiesF(list1)
>>> 1
list1
>>> ['a']
def satisfiesF(L):
temp=L[:]
for x in temp:
if f(x)!=True:
L.remove(x)
return len(L)
def f(s):
return 'a' in s
replace
result = []
for l in L:
result.extend(l)
with
result =L[:]

Return from function shouln't be None

Why does this function result in:
Oops, try again. remove_duplicates([]) resulted in an error: list index out of range"?
my function is as follows
def remove_duplicates(listIn):
listSorted = sorted(listIn)
prevInt = listSorted[0]
listOut = []
listOut.append(prevInt)
for x in listSorted:
if (x != prevInt):
prevInt = x
listOut.append(x)
print listOut
return listOut
remove_duplicates([1,2,3,3,3])
which outputs:
[1, 2, 3]
None
Thank you.
to answer your question you need to just check for the length of your list and return if its empty
if not listIn:
return []
however your whole approach could be simplified with a set like so
def remove_duplicates(listIn):
return list(set(listIn))
output:
>>> print remove_duplicates([1,2,3,3,3])
>>> [1, 2, 3]
of course this is assuming you want to keep your data in a list.. if you dont care then remove the outer list() conversion to make it faster. regardless this will be a much faster approach than what you have written

Is there a difference between the two? [duplicate]

This question already has answers here:
Different behaviour for list.__iadd__ and list.__add__
(3 answers)
Closed 9 years ago.
code A:
lst = [1, 2, 3]
for i in range(10):
lst+= ["42"]
code B:
lst = [1, 2, 3]
for i in range(10):
lst = lst + ["42"]
I know the output is the same, but is there a difference in the way the two lists are built?
What's happening in the back actually?
When you do
lst += ["42"]
You are mutating lst and appending "42" at the end of it. But when you say,
lst = lst + ["42"]
You are creating a new list with lst and "42" and assigning the reference of the new list to lst. Try this program to understand this better.
lst = ["1"]
print(id(lst))
lst += ["2"]
print(id(lst))
lst = lst + ["3"]
print(id(lst))
The first two ids will be the same, bu the last one will be different. Because, a new list is created and lst now points to that new list.
Not knowing the difference between these two will create a problem, when you pass a list as a parameter to a function and appending an item to it, inside the function like this
def mutate(myList):
myList = myList + ["2"] # WRONG way of doing the mutation
tList = ["1"]
mutate(tList)
print(tList)
you will still get ['1'], but if you really want to mutate myList, you could have done like this
def mutate(myList):
myList += ["2"] # Or using append function
tList = ["1"]
mutate(tList)
print(tList)
will print ['1', '2']

Categories