Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why do nested control statements on one line fail in python?

Tags:

python

I have been trying to convert a simple execution such as:

for x in xrange(10):
    if x % 2 == 0:
        print x, 'is even'

to a one liner version:

for x in xrange(10): if x % 2 == 0: print x, 'is even'

which gives me:

  File "foo.py", line 1
    for x in xrange(10): if x % 2 == 0: print x, 'is even'
                      ^
SyntaxError: invalid syntax

I don't see any ambiguity in here. Is there a particular reason why this fails?

like image 939
none Avatar asked Dec 16 '22 18:12

none


2 Answers

It's simply not allowed by the grammar. The relevant productions are:

for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

As you can see, after a for you can either put a simple statement or a "suite", i.e. an indented block. An if statement is a compound statement, not a simple one.

Two lines are the minimum to express this program:

for x in xrange(10):
    if x % 2 == 0: print x, 'is even'

(Of course, you can write equivalent programs that take only one line, such as

for x in xrange(0, 10, 2): print x, "is even"

or any of the other one-liners posted in response to this question.)

like image 90
Fred Foo Avatar answered Feb 24 '23 14:02

Fred Foo


From the formal grammar for 2.7:

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated

if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]

suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | print_stmt  | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | exec_stmt | assert_stmt)

If the suite had allowed a compound_stmt then what you suggest would be accepted. But that would also allow something like this:

if True: try:
  # do something
except:
  # handle
foo()

Is that except outside the enclosing if? Is the call to foo outside the enclosing if? I think this shows that we really don't want in-lining compound statements to be allowed by the formal grammar. Simply adding suite: compound_stmt makes the grammar ambiguous as I read it, where the same code can be interpreted with two or more different meanings, neither disprovable.

Basically, it's by design that what you ask is a parse error. Reworking the formal grammar could allow the code in your example to work without other funny stuff, but it requires careful attention to ambiguity and other problems.

See also Dangling Else, a grammar problem that afflicted the standard Algol-60 language. It's not always easy to find these kinds of problems, so a healthy fear of changing a working grammar is a good thing.

like image 28
wberry Avatar answered Feb 24 '23 15:02

wberry