I have a list of items. In a for loop i check if the item is 3. And if it is 3 then it should change the 3 to a 2. These are the two ways that came to my mind using a for loop. But only the last one does work.
Is there a way I can make the first for loop work without losing its "pythonic" style?
a = [1, 2, 3]
for num in a:
if num == 3:
num = 2
# -> [1, 2, 3]
for i in range(len(a)):
if a[i] == 3:
a[i] = 2
# -> [1, 2, 2]
CodePudding user response:
There's no way to assign to a bare name (num
) and have it affect a container (list a
). You need to use an index. Although, it is more Pythonic to use enumerate
:
for i, num in enumerate(a):
if num == 3:
a[i] = 2
Another option is to use a full-slice assignment to totally overwrite the contents of the list from a generator expression:
a[:] = (2 if num==3 else num for num in a)
CodePudding user response:
Let's take a look at this code:
for i in [1, 2, 3]:
...
In the for loop, the [1, 2, 3]
is a list object. The i
is just a variable that holds a pointer (basically a reference to the data). When you do an operation like i = 3
in the loop, the variable i
is set to hold the number 3
, but the actual list is not changed. List comprehension can be used for what you're trying to accomplish:
a = [1, 2, 3]
l = [2 if num == 3 else num for num in a]
# note that this is a ternary operator in a list comprehension
If you wish to use a for
loop, then then enumerate
method with index assignment will do the trick:
a = [1, 2, 3]
for i, num in enumerate(a):
if num == 3:
a[i] = 2
You can also do it manually like so:
i = 0
for num in a:
if num == 3:
num[i] = 2
i = 1
Note that:
- list comprehension creates a new list (and doesn't edit the old one)
- the enumeration method I showed above does edit the original list (but may be slower, this is based on your machine though)
- the final option I put just to illustrate what the
enumerate
method does and to show that it is an option
CodePudding user response:
To modify in-place, this is perhaps more Pythonic:
for i, n in enumerate(a):
if n == 3:
a[i] = 2
This may also be preferable if you have a lengthy test for how an item is replaced, such that a list comprehension may be unwieldy.
CodePudding user response:
Try a list comprehension:
>>> a = [1, 2, 3]
>>> a = [2 if num == 3 else num for num in a]
>>> a
[1, 2, 2]
CodePudding user response:
If not a list comprehension, you could do use an enumeration:
a = [1,2,3]
for count, num in enumerate(a):
if num == 3:
a[count] = 2
A map also works (seriously just use a list comprehension), but it's a bit less pythonic (lambdas are also confusion to some people):
a = [1,2,3]
a = list(map(lambda num: 2 if num==3 else num, a))
Another thing you can do is, if the variable you're iterating over is some object with a method containing side effects (like a setter), you could use that in-place. For example, imagine I have some myInteger
class that inherits from int
with a method that lets me set some value. Let's say it's something like:
myInt = myInteger(5)
print(myInt.value)
>> 5
myInt.set_value(6)
print(myInt.value)
>>6
This would give you the interface you're looking for:
for num in a:
if num == 3:
a.set_value(2)
The trade-off being that you're writing a weird class to do this, which will lead to confusion in the future.