12

Say I have a function:

x=[]
i=5
while i<=20:
     x.append(i)
     i=i+10
return x

Is there a way to convert it to a list comprehension like this?

newList = [i=05 while i<=20 i=i+10]

I get a syntax error.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
cashmoney11
  • 137
  • 1
  • 1
  • 4
  • 1
    `i` isn't changing in your *while* loop, so it'll simpy *whirl* forever. – Moses Koledoye Mar 09 '17 at 23:25
  • `i` is never modified, so `i <= 5` is always true, and the `while` loop will never exit. – tyteen4a03 Mar 09 '17 at 23:25
  • Possible duplicate of [Using while in list comprehension or generator expressions](http://stackoverflow.com/questions/5505891/using-while-in-list-comprehension-or-generator-expressions) – KoolAid Mar 09 '17 at 23:25
  • 1
    I fixed it, in the real code it's incremented, just forgot to add it here. It still does not work – cashmoney11 Mar 09 '17 at 23:31
  • The fundamental problem here is that initializing an integer and manually incrementing it within a `while` loop is the wrong way to iterate over a sequence of numbers in Python. Your first code block would more appropriately be `x = []; for i in range(5, 21, 10): x.append(i)`, which has a much clearer path for conversion into a comprehension. – TigerhawkT3 Mar 09 '17 at 23:54

3 Answers3

10

You don't need a list comprehension for that. range will just do:

list(range(5, 21, 10)) # [5, 15]

A while loop is not possible inside of a list comprehension. Instead, you could do something like this:

def your_while_generator():
    i = 5
    while i <= 20:
        yield i
        i += 10

[i for i in your_while_generator()]
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Francisco C
  • 8,871
  • 4
  • 31
  • 41
  • You don't need to cast it to `list`. – hashcode55 Mar 09 '17 at 23:39
  • 1
    My code is just an example, I know that that could get me my result, I'm wondering if it's possible to use a while loop inside a list comprehension – cashmoney11 Mar 09 '17 at 23:41
  • @hashcode55 - Without the `list` call, it'll be a `range` object, not a list. – TigerhawkT3 Mar 09 '17 at 23:42
  • 1
    @hashcode55 You don't need to `list` in 2.7, but Python 3 onwards, `xrange` has been renamed to `range` and the original `range` has been dropped. Since `xrange` is a generator and is now called `range` in Python 3, you can do the rest of the math. – Akshat Mahajan Mar 09 '17 at 23:43
  • @hashcode55 - Using an obsolete version of Python, sure, but not for the version of Python that's been around for nearly a decade and gets all the development effort because it's the future of the language. – TigerhawkT3 Mar 09 '17 at 23:45
  • @cashmoney11, no you can't use a while loop, what I'd do would be to make a generator and call it inside of the list comprehension. – Francisco C Mar 09 '17 at 23:45
  • 1
    @AkshatMahajan - Python 2's `xrange` is simply a generator, while Python 3's `range` has additional logic in it (see [here](http://stackoverflow.com/questions/30081275/why-is-1000000000000000-in-range1000000000000001-so-fast-in-python-3)). – TigerhawkT3 Mar 09 '17 at 23:46
  • `list(your_while_generator())` is probably better then the comprehension here. – norok2 Oct 16 '19 at 13:24
9

No, you cannot use while in a list comprehension.

From the grammar specification of Python, only the following atomic expressions are allowed:

atom: ('(' [yield_expr|testlist_comp] ')' |    '[' [testlist_comp] ']' |    '{' [dictorsetmaker] '}' |    NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')

The expression corresponding to a list comprehension - testlist_comp looks like the following in Python 3:

testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )

Here, the only statements allowed are

test: or_test ['if' or_test 'else' test] | lambdef
star_expr: '*' expr
comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter]

where

comp_if: 'if' test_nocond [comp_iter]
comp_iter: comp_for | comp_if

There is not a single while statement allowed anywhere. The only keywords you are allowed to use is a for, for a for loop.

Solution

Use a for loop, or take advantage of itertools.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Akshat Mahajan
  • 7,939
  • 2
  • 30
  • 38
2

There isn't any syntax for this, but you can use itertools. For example:

In [11]: from itertools import accumulate, repeat, takewhile

In [12]: list(takewhile(lambda x: x <= 20, accumulate(repeat(1), lambda x, _: x + 10)))
Out[12]: [1, 11]

(That's not Pythonic though. The generator solution or explicit solution should be preferred.)

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Andy Hayden
  • 291,328
  • 80
  • 565
  • 500