How is `var[:] = []` different from `var = []`? [duplicate] - python

This question already has answers here:
What is the correct way to reassign a list in Python?
(2 answers)
Understanding slicing
(38 answers)
Closed 3 years ago.
I normally understand how slices behave to the left and right of the assignment operator.
However, I've seen this in the Python (3.8.0) manual and trying to figure out what I'm missing.
clear the list by replacing all the elements with an empty list
letters[:] = []
How's that different from just letters = []?
(It's not easy to search for [:] as stackoverflow thinks you're looking for a tag. So, if there is already an answer I couldn't locate it.)
I see some rather irrelevant answers. So, to hopefully clarity, the question is not about what the [:] slice means, rather about assigning the empty list to one.

The assignment var = [] binds the name var to the newly created list. The name var may or may not have been previously bound to any other list, and if it has, that list will remain unchanged.
On the other hand, var[:] = [] expects var to be already bound to a list, and that list is changed in-place.
That's why the behaviour in these two cases is different:
var1 = [1, 2, 3]
var2 = var1
var1 = []
print(var1, var2) # prints [] [1, 2, 3]
var1 = [1, 2, 3]
var2 = var1
var1[:] = []
print(var1, var2) # prints [] []

This code demonstrates what is going on:
original = ['a','b','c']
letters = original
print('Same List')
print(original)
print(letters)
letters = []
print('Different lists')
print(original)
print(letters)
letters = original
letters[:] = []
print('Same list, but empty')
print(original)
print(letters)
Output:
Same List
['a', 'b', 'c']
['a', 'b', 'c']
Different lists
['a', 'b', 'c']
[]
Same list, but empty
[]
[]
The first part of the code: letters = original means that both variables refer to the same list.
The second part: letters = [] shows that the two variables now refer to different lists.
The third part: letters = original; letters[:] = [] starts with both variables referring to the same list again, but then the list itself is modified (using [:]) and both variables still refer to the same, but now modified list.

var = [] is an assignment to the name var. It replaces what, if anything, var used to refer to with [].
var[:] = [] is a method call in disguise: var.__setitem__(slice(), []). It replaces the elements referred to by the slice (in this case, all of them) with the elements in [], effectively emptying the list without replacing it altogether.
Incidentally, you can use var.clear() to accomplish the same thing; slice assignment more generally lets you replace one range of values with another, possibly longer or shorter, range of values.

Related

Deleting a list first vs. just reassigning it

When I have a list I need to clear, I usually just reassign it to a new empty list but is that the correct way or should I 'del' it first
example:
mylist = [1,2,3,4,5]
mylist = []
or
mylist = [1,2,3,4,5]
del mylist
mylist = []
Regards
TL;DR: the first one is good, no need to del first.
Both examples end up in the same exact situation: there's the original list object, which has 0 references to it, and there's a new list object which is bound to the name mylist.

what comes before "for i in range (...)" [duplicate]

This question already has answers here:
Understanding slicing
(38 answers)
Closed 2 years ago.
I have a simple question, looking at the following code:
letters = [hand[i]][:1] for i in range(5)]
What does the argument before 'for I in range(5)' do?? I can't seem to figure it out.
A simple list comprehension has three parts:
my_list = [A for B in C]
This translates exactly into:
my_list = []
for B in C:
my_list.append(A)
So the part before for determines what goes into the list you're creating.
In your case, you could also write it like this:
letters = []
for i in range(i):
letters.append(hand[i][:1]])
The upper piece of code is called list comprehension:
https://docs.python.org/3/tutorial/datastructures.html
So the upper code could be explicitly written out as:
hand # some data. From your code it should be a nested list, eq: hand = [ [...],[...],... ]
letters = []
for i in range(5): # iterates trough 0-4
element = hand[i][:1]
letters.append(element)
So this is just a very short way of constructing a list. You read it out lout like so:
For every i from range(5) take element(s) hand[i][:1] and assign it to a new list letters.
If your question is about the part hand[i][:1], then this is a slice from a nested list. For example:
hand = [
[0,1,2,3],
[4,5,6,7],
...
]
hand[0] == [0,1,2,3]
hand[0][:1] == [0]
hand[1][:1] == [4] # mind it is a slice, so you are left with a list!!

Having trouble with printing mutated variable outside function (Python) [duplicate]

This question already has answers here:
having trouble with lists in python
(5 answers)
Closed 7 years ago.
def satisfiesF(L):
"""
Assumes L is a list of strings
Assume function f is already defined for you and it maps a string to a Boolean
Mutates L such that it contains all of the strings, s, originally in L such
that f(s) returns True, and no other elements
Returns the length of L after mutation
"""
result = []
for l in L:
result.extend(l)
L = list(result)
for i in result:
if i != 'a':
L.remove(i)
return len(L)
Basically what I am trying to do is mutate the list, L within the function. From my testing, it appears that the end result of L is exactly what I am looking for it to be, but when I print L outside of the function it just spits out the original, unmutated list. Can anyone help me fix this issue?
disclaimer: This is a homework problem, but I'm not looking for a solution to the whole problem - just this one issue. Thanks!
You are changing the name L to point to a new list inside the function, in line -
L = list(result)
This does not change the list outside the function, because only the local variable L changed to point to a new list, its previous reference was not changed (And the name/variable outside the function, that was used to call this function still points to the old list).
You should iterate over copy of result (created using result[:]) and remove from result , without changing the reference L points to. And then at end use L[:] = result , to make changes to the list reference that L points to.
Example -
list1 = ['a','b','c','d','e','f']
def satisfiesF(L):
result = []
for l in L:
result.extend(l)
for i in result[:]:
if i != 'a':
result.remove(i)
L[:] = result
return len(L)
satisfiesF(list1)
>>> 1
list1
>>> ['a']
def satisfiesF(L):
temp=L[:]
for x in temp:
if f(x)!=True:
L.remove(x)
return len(L)
def f(s):
return 'a' in s
replace
result = []
for l in L:
result.extend(l)
with
result =L[:]

Removing items from a list in a loop [duplicate]

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
For quite a bit of time now I have been trying to figure out a way to loop through a list and remove the current item that I'm at. I can't seem to get this working as I would like it to. It loops just 1 time through, but I wanted it to loop 2 times. When I remove the removal line - it loops 2 times.
a = [0, 1]
for i in a:
z = a
print z.remove(i)
The output:
[1]
The output that I was expecting:
[1]
[0]
You're changing the list while iterating over it -- z = a doesn't make a copy, it just points z at the same place a points.
Try
for i in a[:]: # slicing a list makes a copy
print i # remove doesn't return the item so print it here
a.remove(i) # remove the item from the original list
or
while a: # while the list is not empty
print a.pop(0) # remove the first item from the list
If you don't need an explicit loop, you can remove items that match a condition with a list comprehension:
a = [i for i in a if i] # remove all items that evaluate to false
a = [i for i in a if condition(i)] # remove items where the condition is False
It is bad practice modify a list while you're looping through it†. Create a copy of the list:
oldlist = ['a', 'b', 'spam', 'c']
newlist = [x for x in oldlist if x != 'spam']
To modify the original list, write the copy back in-place with a slice assignment:
oldlist[:] = [x for x in oldlist if x != 'spam']
† For a gist of why this might be bad practice, consider the implementation details of what goes on with the iterator over the sequence when the sequence changes during iteration. If you've removed the current item, should the iterator point to the next item in the original list or to the next item in the modified list? What if your decision procedure instead removes the previous (or next) item to the current?
The problem is that you're modifying a with remove so the loop exits because the index is now past the end of it.
Don't try to remove multiple items of a list while looping the list. I think it's a general rule you should follow not only in python but also in other programming languages as well.
You could add the item to be removed into a separate list. And then remove all objects in that new list from the original list.

Python wont remove items from list

filtered_list = ['PerezHilton', 'tomCruise', 'q', 'p']
#BIO[user]['follows'] is just a list of strings say ['a', 'b', 'katieh']
#specs is also a string say eg. 'katieh'
for user in filtered_list:
if specs not in BIO[user]['follows']:
filtered_list.remove(user)
The above code for some reson gives this error "ValueError: list.remove(x): x not in list" but clearly 'p' is in the list so why is it not detecting 'p' but it is finding 'q'??
Im soo stumped but any help is appreciated, thanks
** SORRY i FIXED IT NOW *
The list comprehension that does this correctly in one line is at the bottom of the post. Here's some insight into the problem first.
Don't do things like:
for item in list_:
list_.remove(item)
because bad and confusing things happen.
>>> list_ = range(10)
>>> for item in list_:
... list_.remove(item)
...
>>> list_
[1, 3, 5, 7, 9]
Every time you remove an item, you change the indexes for the rest of the items which messes up the loop. One good way to remove items from a list while you're traversing it is to do it by index and work backwards so that removals don't affect the rest of the iterations. This is better because if you remove the 9'th element, then the 8'th element is still the 8'th element but the 10'th element becomes the 9'th element. If you've already dealt with that element, then you don't care what its index is.
>>> list_ = range(10)
>>> for i in xrange(len(list_) - 1, -1, -1):
... del list_[i]
...
>>> list_
[]
Or with a while loop:
i = len(list_)
while i:
i -= 1
del list_[i]
So in your case, the code would look something like
users[:] = [user for user in users if specs in BIO[user]['follows']]
because this is a filtering job and those are best done with list comprehensions. The point of the [:] is that it assigns to a slice of the list instead of clobbering the reference to the list. This means that every other reference to the list will be updated. It's essentially in-place, except that a copy is made before overwriting the original list. For the sake of completeness, here's how to do it with a while loop.
i = len(users)
while i:
i -= 1
if specs not in BIO[users[i]]['follows']:
del users[i]
You could do this if you wanted it done in place. No copy of the list is made here.
Why are you iterating?
>>> un = ['PerezHilton', 'tomCruise', 'q', 'p']
>>> un.remove('p')
>>> un
['PerezHilton', 'tomCruise', 'q']

Categories