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:])
Related
I was reading sets in python http://www.python-course.eu/sets_frozensets.php and got confusion that whether the elements of sets in python must be mutable or immutable? Because in the definition section they said "A set contains an unordered collection of unique and immutable objects." If it is true than how can a set contain the list as list is mutable?
Can someone clarify my doubt?
>>> x = [x for x in range(0,10,2)]
>>> x
[0, 2, 4, 6, 8] #This is a list x
>>> my_set = set(x) #Here we are passing list x to create a set
>>> my_set
set([0, 8, 2, 4, 6]) #and here my_set is a set which contain the list.
>>>
When you pass the set() constructor built-in any iterable, it builds a set out of the elements provided in the iterable. So when you pass set() a list, it creates a set containing the objects within the list - not a set containing the list itself, which is not permissible as you expect because lists are mutable.
So what matters is that the objects inside your list are immutable, which is true in the case of your linked tutorial as you have a list of (immutable) strings.
>>> set(["Perl", "Python", "Java"])
set[('Java', 'Python', 'Perl')]
Note that this printing formatting doesn't mean your set contains a list, it is just how sets are represented when printed. For instance, we can create a set from a tuple and it will be printed the same way.
>>> set((1,2,3))
set([1, 2, 3])
In Python 2, sets are printed as set([comma-separated-elements]).
You seem to be confusing initialising a set with a list:
a = set([1, 2])
with adding a list to an existing set:
a = set()
a.add([1, 2])
the latter will throw an error, where the former initialises the set with the values from the list you provide as an argument. What is most likely the cause for the confusion is that when you print a from the first example it looks like:
set([1, 2])
again, but here [1, 2] is not a list, just the way a is represented:
a = set()
a.add(1)
a.add(2)
print(a)
gives:
set([1, 2])
without you ever specifying a list.
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.
As I am new to programming in Python. I am trying to remove particular elements from array using for loop which looks like
a=[2,3,1,4,1,1,1,5]
n=a.count(1)
for i in range (len(a)-n):
if (a[i]==1):
del a[i]
else:
a[i]=a[i]
print (a)
I want to remove 1 from array a. But, I am getting result as:
[2, 3, 4, 1, 1, 5].
That is 1 still exists in my new array. Can somebody please answer my problem?
try like this:
a = [2,3,1,4,1,1,1,5]
a = [x for x in a if x!=1] # this is called list comprehension
note Never modify list while iterating
Use a while loop and the remove method:
a = [2, 3, 1, 4, 1, 1, 1, 5]
while 1 in a:
a.remove(1)
print a
The real answer to your question (which none of the other answers addresses) is that every time you remove an item, the index i moves past it.
in your case:
a = [2,3,1,4,1,1,1,5]
after deleting the 5th item in the original list, the pointer moves to the 6th item, and the new 5th item (the second 1 in the sequence of three 1s) is skipped.
Regarding the comment never modify a list in a loop, try to implement an in-place algorithm like Fisher-Yates without modifying the list. Never say never. Know what you're doing.
The OP changes the list in-place, not creating a new list.
There are two methods, the second is safe, the first might be faster.
a = [2, 3, 1, 4, 1, 1, 1, 5]
toremove = 1
for i in range(len(a)-1, -1, -1):
if a[i] == toremove:
del a[i]
and
a = [2, 3, 1, 4, 1, 1, 1, 5]
toremove = 1
for i in range(a.count(toremove)):
a.remove(toremove)
The second removes the element however many times it exists (before the loop). Since we are not iterating on the list, it is safe to use the remove method.
Both fragments should be O(n) (but haven't done the calculations).
You can copy a and then remove but you cannot iterate over and delete elements from the same list, if your list starts with n elements python will have n pointers to each element so removing elements from the list as your are iterating over it will cause elements to be missed.python has no way of knowing you have removed elements from the list:
a = [2,3,1,4,1,1,1,5]
for ele in a[:]:
if ele == 1:
a.remove(1)
print(a)
[2, 3, 4, 5]
You can also use reversed which returns and iterator avoiding creating a whole copy of the list at once:
a = [2,3,1,4,1,1,1,5]
for ele in reversed(a):
if ele == 1:
a.remove(1)
print(a)
[2, 3, 4, 5]
Or using a list comprehension with the [:] syntax so we actually update the original object:
a[:] = (ele for ele in a if ele != 1)
All the above are linear operations using a single pass over a.
Actually as the del statement will remove elements from your list , and as the list that you bound in your loop doesn't been update after the first deleting you remove incorrect elements from your list , so if you want to use del you need to make the list name in your loop to reference to new list , that you can use a function for this aim , but as a more python way you can just use a list comprehension:
>>> a=[2,3,1,4,1,1,1,5]
>>> a=[i for i in a if i !=1]
>>> a
[2, 3, 4, 5]
Or you can use filter :
>>> a=[2,3,1,4,1,1,1,5]
>>> a=filter(lambda x: x !=1,a)
>>> a
[2, 3, 4, 5]
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
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