List comprehensions, list of lists and list concatenation - python

This is my code
[temp.append(i.get_hot_post(3)) for i in node_list]
[hot_posts+i.sort(key=sort_by_rate) for i in temp ]
get_hot_posts() return a list of 3 items this way
return recent_posts[0:amount-1]
it could be that the list is shorter than 3 elements and it probably mess the things around but go on
[temp.append(i.get_hot_post(3)) for i in node_list]
after this command, in "temp" i have a list of lists and it's fine.
But when it executes
[hot_posts+i.sort(key=sort_by_rate) for i in temp ]
it gives this error
TypeError: can only concatenate list (not "NoneType") to list

List method sort returns None (just changing list). You can use sorted() function instead.
PS.
[temp.append(i.get_hot_post(3)) for i in node_list]
is not very good idea, cause you will have a list of None. Possible variants:
temp += [i.get_hot_post(3) for i in node_list]
or even
from operator import methodcaller
temp += map(methodcaller(get_hot_post, 3), node_list)

I think you meant sorted(i), no? i.sort() does the sorting in-place and returns nothing.
Also, why would you like to do [hot_posts + ...]? This will not store the values in the hot_posts, so the operation is meaningless, unless you assign the result to a new variable.
I suspect you wanted to do something like
temp = [i.get_hot_post(3) for i in node_list]
hot_posts = [sorted(i, key=sort_by_rate) for i in temp]
Although I have no idea what your last line is supposed to do. Now it just sorts each of these small lists of three and that's it.

Related

Python gives ValueError: list.remove(x): x not in list when removing a tuple from list

Im filtering a tuple like;
newtuple = filter(lambda x: x[2].startswith('902'), csvlist)
Then try to remove that from my original csvlist
csvlist.remove(newtuple) //<--Gives error
but getting;
ValueError: list.remove(x): x not in list
What Im doing wrong here?
Adapting my confirmed comment: filter returns all matches as a sequence, not just the first match, so "newtuple" is a misnomer here, it's really "newtuples" plural (a list of tuples on Py2, a generator of tuples on Py3).
The most straightforward fix is to change your code to:
newtuples = filter(lambda x: x[2].startswith('902'), csvlist)
for newtuple in newtuples: # in list(newtuples) on Py3 to avoid mutating csvlist while iterating
csvlist.remove(newtuple)
but that has some problems; as noted, you'd need to listify the result of filter on Py3, and performance-wise, it's O(n**2); each remove call is O(n), and you could conceivably perform one for every element in csvlist.
A much more efficient, portable, and Pythonic solution is to use a list comprehension to filter the input in a single pass, then replace csvlist's contents with the result of the list comprehension. It's only O(n) work total, and listcomps can avoid the function call overhead of filter+lambda. The improved code is:
csvlist[:] = [x for x in csvlist if x[2].startswith('902')]
That generates the new list, removing all undesired elements as it goes, then replaces the contents of csvlist in place. If you don't have any other references to csvlist that should be updated, you can drop the slice assignment for plain assignment (csvlist = ... rather than csvlist[:] = ...) for a small performance boost.

not able to use append function with list in python [duplicate]

I am trying to append objects to the end of a list repeatedly, like so:
list1 = []
n = 3
for i in range(0, n):
list1 = list1.append([i])
But I get an error like: AttributeError: 'NoneType' object has no attribute 'append'. Is this because list1 starts off as an empty list? How do I fix this error?
This question is specifically about how to fix the problem and append to the list correctly. In the original code, the reported error occurs when using a loop because .append returns None the first time. For why None is returned (the underlying design decision), see Why do these list operations return None, rather than the resulting list?.
If you have an IndexError from trying to assign to an index just past the end of a list - that doesn't work; you need the .append method instead. For more information, see Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add elements to a list?.
If you want to append the same value multiple times, see Python: Append item to list N times.
append actually changes the list. Also, it takes an item, not a list. Hence, all you need is
for i in range(n):
list1.append(i)
(By the way, note that you can use range(n), in this case.)
I assume your actual use is more complicated, but you may be able to use a list comprehension, which is more pythonic for this:
list1 = [i for i in range(n)]
Or, in this case, in Python 2.x range(n) in fact creates the list that you want already, although in Python 3.x, you need list(range(n)).
You don't need the assignment operator. append returns None.
append returns None, so at the second iteration you are calling method append of NoneType. Just remove the assignment:
for i in range(0, n):
list1.append([i])
Mikola has the right answer but a little more explanation. It will run the first time, but because append returns None, after the first iteration of the for loop, your assignment will cause list1 to equal None and therefore the error is thrown on the second iteration.
I personally prefer the + operator than append:
for i in range(0, n):
list1 += [[i]]
But this is creating a new list every time, so might not be the best if performance is critical.
Note that you also can use insert in order to put number into the required position within list:
initList = [1,2,3,4,5]
initList.insert(2, 10) # insert(pos, val) => initList = [1,2,10,3,4,5]
And also note that in python you can always get a list length using method len()
Like Mikola said, append() returns a void, so every iteration you're setting list1 to a nonetype because append is returning a nonetype. On the next iteration, list1 is null so you're trying to call the append method of a null. Nulls don't have methods, hence your error.
use my_list.append(...)
and do not use and other list to append as list are mutable.

How to join multiple rows into single line using python? [duplicate]

I am trying to append objects to the end of a list repeatedly, like so:
list1 = []
n = 3
for i in range(0, n):
list1 = list1.append([i])
But I get an error like: AttributeError: 'NoneType' object has no attribute 'append'. Is this because list1 starts off as an empty list? How do I fix this error?
This question is specifically about how to fix the problem and append to the list correctly. In the original code, the reported error occurs when using a loop because .append returns None the first time. For why None is returned (the underlying design decision), see Why do these list operations return None, rather than the resulting list?.
If you have an IndexError from trying to assign to an index just past the end of a list - that doesn't work; you need the .append method instead. For more information, see Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add elements to a list?.
If you want to append the same value multiple times, see Python: Append item to list N times.
append actually changes the list. Also, it takes an item, not a list. Hence, all you need is
for i in range(n):
list1.append(i)
(By the way, note that you can use range(n), in this case.)
I assume your actual use is more complicated, but you may be able to use a list comprehension, which is more pythonic for this:
list1 = [i for i in range(n)]
Or, in this case, in Python 2.x range(n) in fact creates the list that you want already, although in Python 3.x, you need list(range(n)).
You don't need the assignment operator. append returns None.
append returns None, so at the second iteration you are calling method append of NoneType. Just remove the assignment:
for i in range(0, n):
list1.append([i])
Mikola has the right answer but a little more explanation. It will run the first time, but because append returns None, after the first iteration of the for loop, your assignment will cause list1 to equal None and therefore the error is thrown on the second iteration.
I personally prefer the + operator than append:
for i in range(0, n):
list1 += [[i]]
But this is creating a new list every time, so might not be the best if performance is critical.
Note that you also can use insert in order to put number into the required position within list:
initList = [1,2,3,4,5]
initList.insert(2, 10) # insert(pos, val) => initList = [1,2,10,3,4,5]
And also note that in python you can always get a list length using method len()
Like Mikola said, append() returns a void, so every iteration you're setting list1 to a nonetype because append is returning a nonetype. On the next iteration, list1 is null so you're trying to call the append method of a null. Nulls don't have methods, hence your error.
use my_list.append(...)
and do not use and other list to append as list are mutable.

enumerating list already of type list

I was wondering if some kind soul could explain why I have to convert a list that is already of type list before calling enumerate on it.
>>> l = ['russell', 'bird', 'thomas']
>>> print(type(l))
Type is: `class 'list'`.
If I enumerate over this list without explicitly declaring it as such, this is the output:
>>> print(enumerate(l))
enumerate object at 0x00B1A0F8
When explicitly declaring this list as a list, the output is as expected:
>>> print(list(enumerate(l)))
[(0, 'russell'), (1, 'bird'), (2, 'thomas')]
If someone could help explain why this is the case, it'd be greatly appreciated.
You have two different objects here. Let's say you define a function:
def my_func(my_list):
return 4
If you did my_func(l), of course it wouldn't return a list. Well, enumerate() is the same way. It returns an enumerate object, not a list. That object is iterable, however, so it can be converted to a list. For example:
def my_enumerate(my_list):
index = 0
for item in my_list:
yield index, item
index += 1
That is pretty much how enumerate works. Our function is a generator function and returns a generator object. Since generators are iterable, you can convert it to a list:
print(list(my_enumerate(l))
The Python docs tend to be pretty careful about saying what a function returns, and the docs for enumerate() say that it returns an enumerate object, not a list. (Note that this was already the case in Python 2.)
While it may seem like kind of a pain to have to create a fully-formed list explicitly, like
print(list(enumerate(mylist)))
the advantage of not returning a list is that it can be quicker and use less memory. It is expected that in real-world use, you will generally not need the whole list at once, and will instead be looping over the elements one at a time, such as
for i, elem in enumerate(mylist):
print(i, '->', elem)
You might even not use all of the elements:
mylist = ['russell', 'bird', 'thomas']
for i, elem in enumerate(mylist):
if elem == 'bird':
break
print((i, elem))
So enumerate() gives you the efficiency of generating items only as needed instead of all at once. (Imagine if you were working with a list of every NBA player in history, not just the three in your example.) And on the rare occasions that you really do need all the items at once, it is not that annoying to have to type list(enumerate(mylist)).

Sorting a sublist within a Python list of integers

I have an unsorted list of integers in a Python list. I want to sort the elements in a subset of the full list, not the full list itself. I also want to sort the list in-place so as to not create new lists (I'm doing this very frequently). I initially tried
p[i:j].sort()
but this didn't change the contents of p presumably because a new list was formed, sorted, and then thrown away without affecting the contents of the original list. I can, of course, create my own sort function and use loops to select the appropriate elements but this doesn't feel pythonic. Is there a better way to sort sublists in place?
You can write p[i:j] = sorted(p[i:j])
This is because p[i:j] returns a new list. I can think of this immediate solution:
l = p[i:j]
l.sort()
a = 0
for x in range(i, j):
p[x] = l[a]
a += 1
"in place" doesn't mean much. You want this.
p[i:j] = list( sorted( p[i:j] ) )

Categories