Python list + list vs. list.append() - python

Today I spent about 20 minutes trying to figure out why
this worked as expected:
users_stories_dict[a] = s + [b]
but this would have a None value:
users_stories_dict[a] = s.append(b)
Anyone know why the append function does not return the new list? I'm looking for some sort of sensible reason this decision was made; it looks like a Python novice gotcha to me right now.

append works by actually modifying a list, and so all the magic is in side-effects. Accordingly, the result returned by append is None. In other words, what one wants is:
s.append(b)
and then:
users_stories_dict[a] = s
But, you've already figured that much out. As to why it was done this way, while I don't really know, my guess is that it might have something to do with a 0 (or false) exit value indicating that an operation proceeded normally, and by returning None for functions whose role is to modify their arguments in-place you report that the modification succeeded.
But I agree that it would be nice if it returned the modified list back. At least, Python's behavior is consistent across all such functions.

The append() method returns a None, because it modifies the list it self by adding the object appended as an element, while the + operator concatenates the two lists and return the resulting list
eg:
a = [1,2,3,4,5]
b = [6,7,8,9,0]
print a+b # returns a list made by concatenating the lists a and b
>>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
print a.append(b) # Adds the list b as element at the end of the list a and returns None
>>> None
print a # the list a was modified during the last append call and has the list b as last element
>>> [1, 2, 3, 4, 5, [6, 7, 8, 9, 0]]
So as you can see the easiest way is just to add the two lists together as even if you append the list b to a using append() you will not get the result you want without additional work

Related

TypeError with functions

I am trying to define a function that returns a list without the first and last items in that list. However, I get this when I run the function: "TypeError: 'builtin_function_or_method' object does not support item deletion".
This is my code so far:
def middle(t):
"""returns a copy of a list with the first and last items removed
list -> list"""
t = input
del t[0]
del t[-1]
print(t)
Any help is appreciated.
t=input is assigning t to the function object input. You can't slice a function. t[1:-1] will return a new list with the first and last items removed.
You should delete the t = input line; that's assigning t to the built-in function input, which isn't an array and isn't what you want. Once you do that, you can use:
l = [0, 1, 2, 3, 4]
middle(l)
which will leave l = [1, 2, 3].
However, a better way of doing this is just to say
l = [0, 1, 2, 3, 4]
l2 = l[1:-1]
This leaves l2 as [1, 2, 3], as I assume you wanted.
If I were you, I'd go with something like
themiddlevalues = t[1:-1]
This works on any sort of sequence, and doesn't require a function. It's probably worth learning about python slice notation, as slices are important to numpy, etc. See http://codingbat.com/doc/python-strings.html, as slices work the same way across strings, lists, etc.

function that given a list returns a list of list decreasing

I'd make a function in python, that given a list returns a list of list, in which every element is the list given decreased by one.
Input: list_decreaser([0,3,4,5,6,7,8)
Output: [[0,3,4,5,6,7],[0,3,4,5,6],[0,3,4,5],[0,3,4],[0,3],[0]]
My attempt:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list)
return listresult
The code appends the same list multiple times. It should append copy of the list.
And use del list[..] instead of list.remove(list[..]) to delete an item at specific index.
def list_decreaser(xs):
listresult = []
for i in range(len(xs)-1, 0, -1): # <--- interate backward
del xs[i]
listresult.append(xs[:]) # <----
return listresult
print(list_decreaser([0,3,4,5,6,7,8]))
Or using list comprehension:
>>> xs = [0,3,4,5,6,7,8]
>>> [xs[:i] for i in range(len(xs)-1, 0, -1)]
[[0, 3, 4, 5, 6, 7], [0, 3, 4, 5, 6], [0, 3, 4, 5], [0, 3, 4], [0, 3], [0]]
BTW, don't use list as a variable name. It shadows builtin list function.
The problem is that you're appending the same list over and over again. You keep mutating the list in-place, but you're never creating a new list. So you end up with a list of N references to the same empty list.
This is the same problem discussed in two FAQ questions. I think How do I create a multidimensional list explains it best.
Anyway, what you need to do is append a new list each time through the loop. There are two ways to do that.
First, you can append a copy of the current list, instead of the list itself:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list[:]) # this is the only change
return listresult
This solves your problem, but it leaves a few new problems:
First, list.remove(list[x]) is a very bad idea. If you give it, say, [0, 1, 2, 0], what happens when you try to remove that second 0? You're calling list.remove(0), and there's no way the list can know you wanted the second 0 rather than the first! The right thing to do is call del list[x] or list.pop(x).
But once you fix that, you're removing the elements from the wrong side. x is 0, then 1, then 2, and so on. You remove element 0, then element 1 (which is the original element 2), then element 2 (which is the original element 4), and eventually get an IndexError. Even if you fixed the "skipping an index" issue (which is also explained in the FAQ somewhere), you'd still be removing the first elements rather than the last ones. You can fix that by turning the range around. However, there's an even easier way: Just remove the last element each time, instead of trying to figure out which x is the right thing, which you can do by specifying -1, or just calling pop with no argument. And then you can use a much simpler loop, too:
def list_decreaser(list):
listresult = []
while list:
list.pop()
listresult.append(list[:])
return listresult
Of course this appends the last, empty list, which you apparently didn't want. You can fix that by doing while len(list) >= 1, or putting an if list: listresult.append(list[:]), or in various other ways.
Alternatively, you can make new truncated lists instead of truncating and copying the same list over and over:
def list_decreaser(list):
listresult = []
while len(list):
list = list[:-1]
listresult.append(list)
return listresult
Note that in this second version, rather than changing the value stored in list, we're creating a new list and storing that new list in list.
use this
def list_decreaser(list1):
listresult = []
for i in list1:
list1 = list[:-1]
listresult.append(list1)
return listresult

having trouble understanding this code

I just started learning recursion and I have an assignment to write a program that tells the nesting depth of a list. Well, I browsed around and found working code to do this, but I'm still having trouble understanding how it works. Here's the code:
def depth(L) :
nesting = []
for c in L:
if type(c) == type(nesting) :
nesting.append(depth(c))
if len(nesting) > 0:
return 1 + max(nesting)
return 1
So naturally, I start to get confused at the line with the append that calls recursion. Does anyone have a simple way of explaining what's going on here? I'm not sure what is actually being appended, and going through it with test cases in my head isn't helping. Thanks!
edit: sorry if the formatting is poor, I typed this from my phone
Let me show it to you the easy way, change the code like this:
(### are the new lines I added to your code so you can watch what is happening there)
def depth(L) :
nesting = []
for c in L:
if type(c) == type(nesting) :
print 'nesting before append', nesting ###
nesting.append(depth(c))
print 'nesting after append', nesting ###
if len(nesting) > 0:
return 1 + max(nesting)
return 1
Now lets make a list with the depth of three:
l=[[1,2,3],[1,2,[4]],'asdfg']
You can see our list has 3 element. one of them is a list, the other is a list which has another list in itself and the last one is a string. You can clearly see the depth of this list is 3 (i.e there are 2 lists nested together in the second element of the main list)
Lets run this code:
>>> depth(l)
nesting before append []
nesting after append [1]
nesting before append [1]
nesting before append []
nesting after append [1]
nesting after append [1, 2]
3
Piece of cake! this function appends 1 to the nesting. then if the element has also another list it appends 1 + maximum number in nesting which is the number of time function has been called itself. and if the element is a string, it skips it.
At the end, it returns the maximum number in the nesting which is the maximum number of times recursion happened, which is the number of time there is a list inside list in the main list, aka depth. In our case recursion happened twice for the second element + 1=3 as we expected.
If you still have problem getting it, try to add more print statements or other variables to the function and watch them carefully and eventually you'll get it.
So what this seems to be is a function that takes a list and calculates, as you put it, the nesting depth of it. nesting is a list, so what if type(c) == type(nesting) is saying is: if the item in list L is a list, run the function again and append it and when it runs the function again, it will do the same test until there are no more nested lists in list L and then return 1 + the max amount of nested lists because every list has a depth of 1.
Please tell me if any of this is unclear
Let's start with a couple of examples.
First, let's consider a list with only one level of depth. For Example, [1, 2, 3].
In the above list, the code starts with a call to depth() with L = [1, 2, 3]. It makes an empty list nesting. Iterates over all the elements of L i.e 1, 2, 3 and does not find a single element which passes the test type(c) == type(nesting). The check that len(nesting) > 0 fails and the code returns a 1, which is the depth of the list.
Next, let's take an example with a depth of 2, i.e [[1, 2], 3]. The function depth() is called with L = [[1, 2], 3] and an empty list nesting is created. The loop iterates over the 2 elements of L i.e [1, 2] , 3 and since type([1, 2]) == type(nesting), nesting.append(depth(c)) is called. Similar to the previous example, depth(c) i.e depth([1, 2]) returns a 1 and nesting now becomes [1]. After the execution of the loop, the code evaluates the test len(nesting) > 0 which results in True and 1 + max(nesting) which is 1 + 1 = 2 is returned.
Similarly, the code follows for the depth 3 and so on.
Hope this was helpful.
This algorithm visits the nested lists and adds one for each level of recursion. The call chain is like this:
depth([1, 2, [3, [4, 5], 6], 7]) =
1 + depth([3, [4, 5], 6]) = 3
1 + depth([4, 5]) = 2
1
Since depth([4,5]) never enters the if type(c) == type(nesting) condition because no element is a list, it returns 1 from the outer return, which is the base case.
In the case where, for a given depth, you have more than one nested list, e.g. [1, [2, 3], [4, [5, 6]], both the max depth of [2,3]and [4, [5, 6]] are appended on a depth call, of which the max is returned by the inside return.

Where does the list assignment go?

A number of Python's list methods operate in place and return None (off the top of my head, insert, sort, reverse).
However, there is one behavior that frequently frustrates me. If I create a new list, which normally returns an object, and insert on it at the same time, the new list "disappears":
mytup = (0, 1, 2, 3, 4)
print mytup # (0, 1, 2, 3, 4)
mylist = list(mytup)
print mylist # [0, 1, 2, 3, 4]
newlist = list(mytup).insert(0, 10)
print newlist # None
So if I want to modify a tuple, it requires more lines:
newlist = list(mytup)
newlist.insert(0, 10)
print newlist # [10, 0, 1, 2, 3, 4]
So I have two questions:
Is it correct to say that when I call the list constructor, it returns the object, but when I call the list constructor with a method on it, the method "overrides" the return with None? Again, where does the list go?
Is there a way to insert into a tuple and return a list in one line? I am not trying to play code golf, I just don't think the two lines are logically different enough to merit separation.
insert,sort and reverse modify the list in-place and return None. And in your code you're actually storing that returned value in the newlist variable.
newlist = list(mytup).insert(0, 10)
And that newly created list(created on the fly) is garbage collected as there are no references to it any more.
In [151]: mytup = (0, 1, 2, 3, 4)
In [152]: lis=list(mytup) #create a new list object and add a reference to this new object
In [153]: newlist=lis.insert(0,10) #perform the insert operation on lis and store None
# in newlist
In [154]: print newlist
None
In [155]: print lis
[10, 0, 1, 2, 3, 4] #you can still access this list object with
#the help of `lis` variable.
The answer to your first question has already been given; you assign to the variable the result of the last function call, which is None. Here's the answer to your second question.
Rather than using insert, do something like this:
newlist = [10] + list(mytup)
It creates a new list containing the element to be inserted, appends it to the converted tuple and stores (a reference to) the resulting list.
This, of course, only works if you want to insert on either end.
If you need the new element to be inserted somewhere else, you have to slice the tuple, e.g. to insert after the third element in the tuple:
newlist = list(mytup[:3]) + [10] + list(mytup[3:])

list extending in python

i have just confronted this situation which i am not able to understand:
In [3]: nk1=range(10)
In [4]: nk2=range(11,15)
In [5]: nk1.extend(nk2)
In [6]: nk1
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14]
In [7]: dir(range(10))==dir(list)==dir(range(11,15))==dir(nk1)==dir(nk2)
Out[7]: True
In [8]: print range(10).extend(range(11,15))
None
as you can see above that i can easily extend nk1, but why not the last statement which is returning None?? why it is returning None in In[8] input (while from In[7] we can see that all are same)??? so do i always have to make instance of range to extend it???
from python docs; i found this; but i am not getting how does above case happened.
When you extend a list the list is modified in-place. A new list is not returned.
In-place modifications indicate that the object whose method you're calling is going to make changes to itself during that method execution. In most cases (to avoid the confusion you're currently encountering) these types of operations do not return the object that performed the operation (though there are exceptions). Consider the following:
list1 = range(10)
list2 = range(10) + range(11,15) # no in place modification here.
nolist1 = list1.extend(range(11,15)) # list1 is now (0 ... 14)
nolist2 = range(10).extend(range(11,15)) # Nothing actually happens here.
In the final example, the range function returns a list which is never assigned to anything. That list is then extended, the result of which is a return value of None. This is printed.
range() gives out a handle to the newly created list. If you need to modify it, you have to store the handle to a variable.
It is not printing 'None' because extend doesn't return anything. As you are using range(), there is no way to get the output directly.
nk1 = range(10)
nk1.extend(range(11,15))
print nk1

Categories