F.M.F.
F.M.F.

Reputation: 2292

Python: list comprehension strange behaviour

Why is this code:

x = {"first":(True, "first description"), "second":(False, "second description")}
items = ["first", "second"]

for element in [k in x and x[k][0] for k in items]:
    print element

printing

instead of the elements in the "items"-list that match the expression

k in x and x[k][0]

Upvotes: 3

Views: 171

Answers (4)

shizhz
shizhz

Reputation: 12501

Because the expression to generate new list in [k in x and x[k][0] for k in items] is k in x and x[k][0], and the evaluation of and process is that it'll split the whole expression by and, then evaluate each "sub-expression", return the first falsy value or the last value no matter whatever it is. In this case it'll first evaluate k in x, which will always be true, then it will return the result of x[k][0], which generates the final result:

[True, False]

If you try [k in x and x[k][0] for k in items], you'll get result:

['first description', 'second description']

To filter element, you need to put the test condition at the tail of the whole expression like:

[k for k in items if k in x and x[k][0]]

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1121874

You are printing the outcome of the expression k in x and x[k][0].

That's either going to be False (if the outcome of the k in x condition is false), or the outcome of x[k][0] (if the outcome of k in x was true). Since x[k][0] is a boolean object in your input sample, you'll always get boolean values here; nowhere will the list comprehension produce the keys instead.

If you wanted to filter, use an if statement after the loop:

[k for k in items if k in x and x[k][0]]

This then produces a list with k values for which the expression produces true, which for your sample data matches just one element, ['first'], because only for that key is the first element in the tuple set to True.

Upvotes: 4

binu.py
binu.py

Reputation: 1216

x = {"first":(True, "first description"), "second":(False, "second description")}
items = ["first", "second"]

for element in [k in x and x[k][0] for k in items]:
    print element

Lets try to understand what result it gives you from the list comprehension.

[k in x and x[k][0] for k in items]

Lets observe it from the right side. for k in items == ['first', 'second']

1st iteration

k = 'first'

['first' in x (True) and x['first'][0] (True)]

[True and True]

[True]

2nd Iteration

k = 'second'

list is [True]

[True, 'second' in x (True) and x['second'][0] (False)]

[True, True and False]

[True, False]

Now,

for element in [True, False]:
    print element

It should print,

True

False

which is what it is doing.

Upvotes: 2

L3viathan
L3viathan

Reputation: 27283

[k in x and x[k][0] for k in items] returns the boolean expression k in x and x[k][0] for every element in items, which will be True for every element whose first element is truthy.

What you probably mean is:

[k for k in items if k in x and x[k][0]]

Upvotes: 3

Related Questions