This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Closed 5 months ago.
Working my way into Python (2.7.1)
But failing to make sense (for hours) of this:
>>> a = [1, 2]
>>> b = [3, 4]
>>>
>>> a.extend([b[0]])
>>> a
[1, 2, 3]
>>>
>>> a.extend([b[1]])
>>> a
[1, 2, 3, 4]
>>>
>>> m = [a.extend([b[i]]) for i in range(len(b))] # list of lists
>>> m
[None, None]
The first two extends work as expected, but when compacting the same in a list comprehension it fails.
What am i doing wrong?
extend modifies the list in-place.
>>> [a + b[0:i] for i in range(len(b)+1)]
[[1, 2], [1, 2, 3], [1, 2, 3, 4]]
list.extend() extends a list in place. Python standard library methods that alter objects in-place always return None (the default); your list comprehension executed a.extend() twice and thus the resulting list consists of two None return values.
Your a.extend() calls otherwise worked just fine; if you were to print a it would show:
[1, 2, 3, 4, 3, 4]
You don't see the None return value in the Python interpreter, because the interpreter never echoes None results. You could test for that explicitly:
>>> a = []
>>> a.extend(['foo', 'bar']) is None
True
>>> a
['foo', 'bar']
the return value of extend is None.
extend function extends the list with the value you've provided in-place and returns None. That's why you have two None values in your list. I propose you rewrite your comprehension like so:
a = [1, 2]
b = [3, 4]
m = [a + [v] for v in b] # m is [[1,2,3],[1,2,4]]
For python lists, methods that change the list work in place and return None. This applies to extendas well as to append, remove, insert, ...
In reply to an older question, I sketched an subclass of list that would behave as you expected list to work.
Why does [].append() not work in python?
This is intended as educational. For pros and cons.. look at the comments to my answer.
I like this for the ability of chaining methods and working in a fluent style, e.g. then something like
li = FluentList()
li.extend([1,4,6]).remove(4).append(7).insert(1,10).reverse().sort(key=lambda x:x%2)
would be possible.
a.extend() returns None.
You probably want one of these:
>>> m = a + b
>>> m
[1, 2, 3, 4]
>>> a.extend(b)
>>> a
[1, 2, 3, 4]
Aside from that, if you want to iterate over all elements of a list, you just can do it like that:
m = [somefunction(element) for element in somelist]
or
for element in somelist:
do_some_thing(element)
In most cases there is no need to go over the indices.
And if you want to add just one element to a list, you should use somelist.append(element) instead of `somelist.extend([element])
Related
This is a question out of curiosity rather than trying to use it for a practical purpose.
Consider I have the following simple example where I generate a list through list comprehension:
>>> a = [1, 2, 3]
>>> b = [2 * i for i in a]
>>> b
[2, 4, 6]
>>> b.append(a)
>>> b
[2, 4, 6, [1, 2, 3]]
However if I try and do this all in one action
>>> a = [1, 2, 3]
>>> b = [2 * i for i in a].append(a)
>>> b == None
True
The result returns None. Is there any reason why this is the case?
I would have thought that an action like this would either return an answer like in the first example or throw an error.
For reference I'm using Python 3.6.5
append only works on variables, not list literals, since it updates the list object itself and does not return the resulting list.
As #Tomalak mentioned noted, running a similar operation on a simple list also returns None
>>> [1, 2, 3].append(4) == None
True
You can use concatination + instead of append in list comprehension
In [1]: a = [1, 2, 3]
In [2]: b = [2 * i for i in a] + [a]
In [3]: b
Out[3]: [2, 4, 6, [1, 2, 3]]
#ScottMcC, methods defined on mutable objects like list, dictionary mostly perform operations on calling object and doesn't return anything.
In case of immutable object like string you may see, methods return the modified form(a different object) of the calling object. In case of list, it's different.
You can't expect the below operations on list kind of mutable objects.
s = "hello DJANGO"
s2 = s.upper()
s3 = s.lower()
print(s) # hello DJANGO
print(s2) # HELLO DJANGO
print(s3) # hello django
Now, have a look at the below examples.
list is mutable object.
Calling sort() method on list directly modified the calling object and doesn't return anything (That's why None).
Calling sorted() function doesn't alter the passing list. It creates a separate sorted list based on the passed list. As it is not a method defined on list object, it returns the new sorted list.
append() method appends item on the calling list and doesn't return anything. Once you call it, you are done with updating (appending an item) the list.
# sort() method defined on list updates the calling list
# As it updates current list, it doesn't return anything. That's why None.
a = [5, 8, 1, 2, 7]
n = a.sort()
print (a)
print(n)
print ()
# sorted() function returns a new sorted list
# It doesn't update the calling list a2
a2 = [5, 8, 1, 2, 7];
n = sorted(a2);
print (a2)
print(n)
print()
# append() is method defined on list, it updates calling list so it doesn't return anything (None)
l = []
n = l.append(34)
print(l)
print (n)
Output
[1, 2, 5, 7, 8]
None
[5, 8, 1, 2, 7]
[1, 2, 5, 7, 8]
[34]
None
Is it possible to do an action with the item in a list comprehension?
Example:
list = [1, 2, 3]
list_filtered = [ i for i in list if i == 3 AND DO SOMETHING WITH I]
print (list_filtered)
For example if I want to remove the '3' how would I go about it? Logic says that it's something like:
list = [1, 2, 3]
list_filtered = [ i for i in list if i == 3 && list.remove(i) ]
print (list_filtered)
I can't seem to make Python perform an action with that 'i' with any syntax that I tried. Can someone please elaborate?
EDIT: Sorry, the explanation might not be clear enough. I know how to iterate and create the new list. I want to create "list_filtered" and remove that value from "list" if it fits the "IF" statement.
Practically I need the following:
list = [1, 2, 3]
list_filtered = [ i for i in list if i == 3 && list.remove(i) ]
print (list_filtered)
# output >> [3]
print (list)
# output >> [1, 2]
I hope the above makes it more clear. Also, please note that my question is if this can be done in the list comprehension specifically. I know how to do it with additional code. :)
EDIT2: Apparently what I wanted to do isn't possible and also isn't advisable (which is the reason it isn't possible). It seemed like a logical thing that I just didn't know how to do. Thanks guys :)
If you're just trying to remove 3 you could do:
list_filtered=[i for i in list if i != 3]
print(list_filtered) # [1,2]
This will remove all values that are not equal to 3.
Alternatively if you wanted to do something like increment all the items in the list you would do:
[i+1 for i in list]
>>> [2,3,4]
Using a function on every item of the list would look like:
[float(i) for i in list]
>>> [1.0, 2.0, 3.0]
You can do ternary statements:
[i if i<3 else None for i in list]
>>>[1, 2, None]
and a whole lot more...
Here is more documentation on list comprehensions.
Given your new updates, I would try something like:
list_filtered=[list.pop(list.index(3))]
Then list_filtered would be [3] and list would be [1,2] as your specified.
Your miss understand the purpose of list comprehension. List comprehension should be used to create a list, not to use its side effects. Further more, as Leijot has already mentioned, you should never modify a container while iterating over it.
If you want to filter out certain elements in a list using list comprehension, use an if statement:
>>> l = [1, 2, 3]
>>> l_filtered = [i for i in l if i != 3]
>>> l_filtered
[1, 2]
>>>
Alternatively, you can use the builtin function filter():
>>> l = [1, 2, 3]
>>> l_filtered = list(filter(lambda x: x != 3, l))
>>> l_filtered
[1, 2]
>>>
Edit: Based on your most recent edit, what your asking is absolutely possible. Just use two different list comprehensions:
>>> l = [1, 2, 3]
>>> l_filtered = [i for i in l if i == 3]
>>> l_filtered
[3]
>>> l = [i for i in l if i != 3] # reassigning the value of l to a new list
>>> l
[1, 2]
>>>
Maybe you want to provide a different example of what you're trying to do since you shouldn't remove elements from a list while you're iterating over it. Since list comprehensions create another list anyway, you could accomplish the same thing as your example by building a list that omits certain elements. If your goal is to create a list that excludes the value 3, to slightly modify your example:
l = [1, 2, 3]
list_filtered = [ i for i in l if i != 3 ]
l remains unaltered, but list_filtered now contains [1, 2].
This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Alternatives to using in-place list methods within a list comprehension?
(3 answers)
Closed 6 months ago.
I am just trying to understand what happens during list comprehension. Some methods which work on lists 'in-place' don't seem to work when applied in a list comprehension:
a = [[1, 2, 3], [4, 5, 6]]
i1 = id(a[0])
for i in a: i.reverse()
>>> [[3, 2, 1], [6, 5, 4] # Works
print i1 == id(a[0]) # True, same memory address
a = [i.reverse() for i in a]
>>> [None, None] # Doesn't work
print i1 == id(a[0]) # False, new memory address
a = [i[::-1] for i in a]
>>> [[3, 2, 1], [6, 5, 4]] # Works!
print i1 == id(a[0]) # False
I am guessing this has something to do with all the elements getting copied to a different memory space. Why does i[::-1] work whereas i.reverse() doesn't?
i.reverse() reverses the array in place and doesn't return anything, meaning it returns None type. That way you obtain [None, None] from list comprehension and previous arrays' elements are reversed at the same time.
These two shouldn't be mixed, either use a for and x.reverse(), or use reversed(x) or x[::-1] in a list comprehension.
i.reverse() reverses the list in-place and returns None.
What the docs say:
list.reverse()
Reverse the elements of the list, in place
vs.
reversed(seq)
Return a reverse iterator. seq must be an object which has a
__reversed__() method or supports the sequence protocol
(the __len__() method and the __getitem__() method with integer arguments starting at 0).
Examples:
>>> xs = [1, 2, 3]
>>> id(xs)
140625121860208
>>> ys = xs[::-1]
>>> id(ys)
140625121924088
Slicing creates a new list.
>>> xs.reverse()
>>> xs
[3, 2, 1]
>>> id(xs)
140625121860208
In-place sorting/reversing retains the original list.
>>> zs = list(reversed(xs))
>>> zs
[1, 2, 3]
>>> id(zs)
140625121976400
reversed() returns an iterator; which when turns into a list creates a new list! If you have a read of PEP 0322 -- Reverse Iteration you'll note that reversed() does not create a new data structure but simply iteratoes over the sequence in reverse order.
This does what you intend:
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> [list(reversed(i)) for i in a]
[[3, 2, 1], [6, 5, 4]]
List comprehension always returns a new list, so using the in-place reverse
method just returns the return value of reverse, which is None.
The function reversed() gives you an new iterator. Converting it to
a list is for your example the same as:
>>> [i[::-1] for i in a]
Even though they look very similar, it is important to distinguish both,
the function reversed() and the method obj.reverse()
list.reverse reverses a list in place, and return None.
while slice a[::-1] creates another list and return as value.
list comprehension will take the return value of each expression.
This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Closed 5 months ago.
I have something similar to:
>>> S=list()
>>> T=[1,2,3]
>>> for t in T:
... print(S.append(t))
The output I am getting is:
...
None
None
None
I expect S contains t. Why this is not working with me ?
list.append() does not return anything. Because it does not return anything, it default to None (that is why when you try print the values, you get None).
It simply appends the item to the given list in place. Observe:
>>> S = list()
>>> T = [1,2,3]
>>> for t in T:
... S.append(t)
>>> print(S)
[1, 2, 3]
Another example:
>>> A = []
>>> for i in [1, 2, 3]:
... A.append(i) # Append the value to a list
... print(A) # Printing the list after appending an item to it
...
[1]
[1, 2]
[1, 2, 3]
. append() is a list method, that does not return a value it alters the list. For example .index() or .count() methods return object values, while .append() alters the object. For example:
T = [1, 2, 3]
T.append(4)
print(T)
Result:
[1, 2, 3, 4]
We can use .append() to change the list S and add elements from the list T. Lists S, and T are two separate objects with two different addresses in the memory. With the function id() you can check that.
T = [1, 2, 3]
print(id(T))
S = list()
print(S)
print(id(S))
for t in T:
S.append(t)
print(S)
print(id(S))
Result:
2476978999688
[]
2476978081224
[1, 2, 3]
2476978081224
If you want to use only two different names (S and T) for the same list, we can write:
print(T)
print(id(T))
S = T
print(S)
print(id(S))
Result:
[1, 2, 3]
2476978999688
[1, 2, 3]
2476978999688
At the interpreter,
a = [1,2,3,4]
a = a.reverse()
Next when I type a at the interpreter, I get nothing. So it seems a = a.reverse() generates an empty list. Is this by design?
I am using python 2.5 on windows xp.
list.reverse() modifies the list in-place, returns None. But if you want to protect old list, you can use reversed() function for that, it returns an iterator.
In [1]: a=[1,2,3,4]
In [2]: print(a.reverse())
None
In [3]: a
Out[3]: [4, 3, 2, 1]
In [4]: a=[1,2,3,4]
In [5]: print(reversed(a))
<listreverseiterator object at 0x24e7e50>
In [6]: list(reversed(a))
Out[6]: [4, 3, 2, 1]
In [7]: a
Out[7]: [1, 2, 3, 4]
reverse changes list in-place, and doesn't return anything. Thus, this is the expected usage:
a = [1, 2, 3, 4]
a.reverse()
a # => [4, 3, 2, 1]
If you assign the result of reverse back to a, you will overwrite all its hard work with the nonsensical return value (None), which is where your bug comes from.
list is a mutable type, so list operations are in-place, and return None.
The built-in method reverse of a list on python doesn't return the reversed list.
It reverses the list in place.
So, if you want to reverse your list, like in your code, just do:
a = [1,2,3,4]
a.reverse()
list.reverse() just doesn't return anything, because it changes the list in-place. See this example:
>>> a = [1,2,3,4]
>>> a.reverse()
>>> a
[4, 3, 2, 1]
There also is the reversed function (actually a type, but doesn't matter here), which does not change the list in-place, but instead returns an iterator with the list items in the reverse order. Try:
>>> a = [1,2,3,4]
>>> a = list(reversed(a))
>>> a
[4, 3, 2, 1]
I think what you want to do is:
a = [1,2,3,4]
a.reverse()
a is an object and the operations work on it's data, so you don't need to assign again it.
The reverse method does the reverse 'in place' (like sort) and returns None, so after calling a.reverse() a already contains the result.