duckkkk
duckkkk

Reputation: 51

Confusion with unpacking in python

This is basically a code golf problem I was solving. The statement goes like so:

Given N, X and a line with X numbers between 0 and 9. Print all valid numbers between 0 and N inclusive. A number is valid if all of your digits are in line. If the sequence is empty, print NONE.

And this was my first attempt:

    n,x=map(int,input().split())
    d=set(input().split())
    print(*[v for v in range(n+1)if not set(str(v)).difference(d)]or'NONE')

Why does python unpack the string 'NONE' here? Isn't this supposed to be a simple short circuit where python prints the unpacked list if it is not empty or otherwise print the full string intact?

Upvotes: 0

Views: 284

Answers (2)

ForceBru
ForceBru

Reputation: 44848

Apparently, Starred has higher precedence:

>>> import ast
>>> print(ast.dump(ast.parse("print(*[] or 'NONE')"), indent=2))
Module(
  body=[
    Expr(
      value=Call(
        func=Name(id='print', ctx=Load()),
        args=[
          Starred(
            value=BoolOp(
              op=Or(),
              values=[
                List(elts=[], ctx=Load()),
                Constant(value='NONE')]),
            ctx=Load())],
        keywords=[]))],
  type_ignores=[])

As you can see, Starred is applied to BoolOp, which is the or operator with two operands: the list and the string 'NONE'. So you could put parentheses like this:

print(*( [] or 'NONE'))

In words, unpacking will be applied to the result of [<your list here>] or 'NONE'.

Note: I've replaced your list comprehension with an empty list [] for the sake of brevity.

Upvotes: 1

bereal
bereal

Reputation: 34282

If you try a simpler example, e.g.:

print(*[] or 'Test')

you'll see:

T e s t

It's because the call is actually parsed as:

print(*([] or 'Test'))

Due to precedence rules. Otherwise, the expression print((*[]) or 'Test') wouldn't even make any sense. Instead, try:

print(*[] or ['Test'])

and that will work like you expected.

Upvotes: 1

Related Questions