7

I would like to multiply all elements of a list by a number. I know the other ways to do it but I want to know Why isn't this working? I am getting the very same list as an output.

lst = eval(input('enter a list'))
for num in lst:
     num = num * 2
print(lst)
Remi Guan
  • 21,506
  • 17
  • 64
  • 87
AdR
  • 197
  • 1
  • 3
  • 10
  • You are only updating the loop variable, not the contents of the list... – OneCricketeer Feb 12 '16 at 05:56
  • Do you need to change `lst` itself, or will a new list be fine? – hpaulj Feb 12 '16 at 06:02
  • 2
    To all the answerers: I think OP knows *how to do this*, and this question is about *why doesn't this work* not *how can I get this works*. – Remi Guan Feb 12 '16 at 06:11
  • 4
    there are two proper answers to the question with 0 upvotes @KevinGuan – midori Feb 12 '16 at 06:14
  • Possible duplicate of [How to multiply individual elements of a list with a number?](https://stackoverflow.com/questions/8194959/how-to-multiply-individual-elements-of-a-list-with-a-number) – Georgy Apr 03 '19 at 09:06

7 Answers7

6

It isn't working because, you're using for loop on a list and defining/changing the num global variable, not the elements in lst list.

For example:

>>> l = [1, 5, 8]
>>> for num in l:
...     num *= 2
...     
... 
>>> num
16
>>> l
[1, 5, 8]

In this case, in the first loop, num is 1 (the first element in l), and sure 1 * 2 gives 2.

Then, num become 5 since 5 is the second element in the list. After num * 2, num become 10.

In the second for loop, it become 8 * 2, 16. it doesn't change again because the for loop is ended.

However, you didn't change anything of the list during this loop. Because for only gets the elements in the list, and put it into a temporary variable.

And when you change that temporary variable inside the for loop, you didn't change anything of the list.

Remi Guan
  • 21,506
  • 17
  • 64
  • 87
4

Well, since you wrote "I know the other ways to do it but I want to know Why isn't this working?", here is your answer: You are only modifying the temporary loop variable num, not the list itself. Try this:

for i, num in enumerate(lst):
     lst[i] = lst[i] * 2
Selcuk
  • 57,004
  • 12
  • 102
  • 110
  • Not sure why `enumerate` is being used when `range(len(lst))` would just iterate over the indices. Is there a reason? – Jared Goguen Feb 12 '16 at 06:08
  • 1
    No, but this is a generic solution that is applicable when you also need the variable `num`. You could also write `lst[i] = num * 2` for instance. – Selcuk Feb 12 '16 at 06:28
4

You are not changing the elements of the lst, do it like this:

for num in range(len(lst)):
    lst[num] *= 2
midori
  • 4,807
  • 5
  • 34
  • 62
3
lst=eval(input('enter a list'))
lst=list(map(lambda num:num*2,lst))
print (lst)

You need to update the lst.

vks
  • 67,027
  • 10
  • 91
  • 124
  • 1
    In Python 3.X, `map(...)` will return a `map` object that will need to be explicitly converted into a list. – Jared Goguen Feb 12 '16 at 06:06
  • 1
    Thanks for the solution @vks Map makes it very concise,will read about it. – AdR Feb 12 '16 at 06:51
  • @vks: I didn't downvote, but maybe because...please check my comment below the question and my answer. By the way, `map` is slower than *list comprehension*, please also check [Anton Protopopov's answer](http://stackoverflow.com/a/35355780/5299236) about it. – Remi Guan Feb 12 '16 at 12:42
3

It's better to use ast.literal_eval then eval due to safety. You could read about it here. You could solve that with list comprehension:

import ast
lst = ast.literal_eval(input('enter a list'))
lst = [num*2 for num in lst]
print(lst)

List comprehensions are faster then map with lambda. Timing:

lst = list(range(1000))

In [56]: %timeit list(map(lambda num:num*2,lst))
10000 loops, best of 3: 169 us per loop

In [57]: %timeit [num*2 for num in lst]
10000 loops, best of 3: 80.5 us per loop
Community
  • 1
  • 1
Anton Protopopov
  • 30,354
  • 12
  • 88
  • 93
3

Two clean expressions:

In [360]: lst=list(range(10))

to change in-place:

In [361]: for i,v in enumerate(lst):
   .....:     lst[i]=v*2
   .....:     
In [362]: lst
Out[362]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

to produce a new list:

In [363]: [v*2 for v in lst]
Out[363]: [0, 4, 8, 12, 16, 20, 24, 28, 32, 36]

For input I'd prefer explicitly splitting and converting a string:

In [365]: lst = [int(x) for x in input().split()]
10 11 0 1 2 
In [366]: lst
Out[366]: [10, 11, 0, 1, 2]
hpaulj
  • 221,503
  • 14
  • 230
  • 353
2

As everyone else pointed out, the correct way to do this is by indexing into the list:

myList = range(5)
for i in range(len(myList)):
    myList[i] *= 2
print myList #[0,2,4,..]

A Little Bit About Pass By Assignment

Your loop, which uses the for num in list notation does not modify the list. Because the loop variable num takes a value of type int at each iteration of the loop, since ints are immutable (i.e its value cannot be changed) in python, num gets a copy of the integer value.

This changes when the object in the list is mutable and is passed by reference. Consider the following:

class X: 
    def __init__(self):
         self.val = 1

myList = [X() for i in range(5)]
print [element.val for element in myList] #1,1,1,1,1

for el in myList:
    el.val = 2

print [element.val for element in myList] #2,2,2,2,2 

Now, since myList contains a list of X objects which are mutable, the loop variable el has a reference copied into it. The reference points to the same object in memory that the reference in original myList points to. So when you change the object using the loop variable, the objects referred to in the original myList are also changed.

This Pass By Reference talks about it in greater depth.

Community
  • 1
  • 1
Garrett R
  • 2,687
  • 11
  • 15
  • 1
    Besides being mutable, you also have to mutate, which your `el.val = 2` does do; for completeness, you could mention that `el = 2` would *not* update the list element (as you are no longer mutating, but replacing). – Ethan Furman Mar 07 '16 at 21:35