I have been given the following piece of code:
def two_pair(ranks):
"""If there are two pair, return the two ranks as a
tuple: (highest, lowest); otherwise return None."""
pair = kind(2,ranks)
lowpair = kind(2, list(reversed(ranks)))
if pair and lowpair != pair:
return (pair,lowpair)
else:
return None
In the lowpair variable, why does list() need to be stated? Why can't you just say reversed(ranks). ranks is a list. Is it not already implied?
reversed returns an iterator, not a list. We need to explicitly convert that to a list, unless we just want to iterate it.
a = [1, 2, 3]
print reversed(a) # <listreverseiterator object at 0x7fc57d746790>
That is why we have to use list to get the actual reversed list, like this
print list(reversed(a)) # [3, 2, 1]
If you want shorter code you could do ranks[::-1] instead of list(reversed(ranks)).
>>> ranks = [1,2,3]
>>> ranks[::-1]
[3, 2, 1]
reversed(ranks) isn't a reversed list. It's an iterator:
>>> reversed([1, 2, 3])
<listreverseiterator object at 0x0000000001DDE9E8>
The list call is necessary to get a reversed list.
Related
Given this source list:
source_list = [2, 3, 4]
and this function:
def function(list_in):
list_in.append(5)
list_in.insert(0, 1)
return list_in
As expected, I get:
>>> function(source_list)
[1, 2, 3, 4, 5]
But if I call the variable source_list outside of the function I still get:
>>> source_list
[1, 2, 3, 4, 5]
Is there an alternative way of modifying (specifically appending/prepending to) a list within a function such that the original list is not changed?
If you are the caller of the function, you can copy first
new_list = function(source_list[:])
This has the advantage that the caller decides whether it wants its current list to be modified or not.
If you have access to the function, you can copy the list passed:
like this:
def function(list_in_): # notice the underscore suffix
list_in = list_in_[:] # copy the arg into a new list
list_in.append(5)
list_in.insert(0, 1)
return list_in # return the new list
otherwise you can call the function with a copy of your source_list, and decide if you want a new list, or if you prefer the source_list mutated, as demonstrated by #tdelaney
You can use the copy() method on the list:
new_list = function(source_list.copy())
Add just a line to your function:
def function(list_n):
#Do a copy of your list
list_in = list_n.copy()
list_in.append(5)
list_in.insert(0, 1)
return list_in
Try this. Since the source list is modified while running the function in your code you are not able to retain the source list.
source_list = [2, 3, 4]
list_in=source_list.copy()
def function(list_in):
list_in.append(5)
list_in.insert(0, 1)
return list_in
print(function(list_in))
print(source_list)
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
I'm trying to add the last item of a list onto the list my code is:
def dup_last(data):
data.append([-1])
return data
and calling for the function is:
item = dup_last([1,2,3])
print(item)
but I want my output to be within only one set of brackets like:
[1, 2, 3, 3]
data.append([-1])
Here you are appending [-1], a list with an element of -1, change it to:
data.append(data[-1])
In addition to other answers, I would also suggest to use slicing notation [:] when dealing with lists to prevent getting list index out of range errors in case there is no item:
def dup_last(data):
data.append(data[-1])
return data
The above function will raise IndexError if data is empty list:
>>> print dup_last([])
----> 2 data.append(data[-1])
3 return data
4
IndexError: list index out of range
When you update your function as follows, you no longer get that kind of error:
def dup_last(data):
data.extend(data[-1:])
return data
>>> print dup_last([])
[]
>>> print dup_last([1])
[1, 1]
>>> print dup_last([1, 2])
[1, 2, 2]
There is a good explanation in this SO question about how slicing works in Python.
You need to do data.append(data[-1]); data.append([-1]) appends a value which is a list containing only -1, so your result will be [1, 2, 3, [-1]].
Note that this will modify the list in-place, so whichever list you pass in will also have the last element duplicated, not just the list you get out (though they could be the same list).
I wouldn't use a function for this; just do data.append(data[-1]) instead of data = dup_last(data), or even dup_last(data). Also, it's probably better to just add the duplicate manually if you're working with a list literal; data = [1, 2, 3, 3] vs data = dup_last([1, 2, 3]) or similar.
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.
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.