sof
sof

Reputation: 9649

Tidy up list comprehensions pythonically

FP like Haskell can bind a (var) name trivially e.g:

[(g y, h y) | x <- mylist, let y = f x]

Python does it possibly below:

mylist = [f(x) for x in mylist]
mylist = [(g(y), h(y)) for y in mylist]

Walrus assignment in Python 3.8 seems a hack to simplify list comprehensions :

[(y := f(x), g(y), h(y)) for x in mylist]

What is so far considered pythonic way in this case?

Upvotes: 2

Views: 182

Answers (4)

Jim Newton
Jim Newton

Reputation: 652

Some other questions are linked here (such as python how to declare variable in for comprehension without iterating) so let me answer a slightly more general question.

How to bind a local variable within a for comprehension without iteration. The trick is to create a singleton list and iterate over it such as for b in [u(a)] in the following.

One might find this ugly and cryptic, but it is the most concise way I have found.

[x for a in f(x) for b in [u(a)] if v(b) for x in w(b)]

Upvotes: 0

kaya3
kaya3

Reputation: 51034

Since Python doesn't have "let ... in ..." expressions, the cleanest way is to write a function and call it.

def apply_g_h(x):
    y = f(x)
    return g(y), h(y)

mylist = [ apply_g_h(x) for x in mylist ]

If you really prefer it as a one-liner, then a lambda function can serve the same purpose:

mylist = [ (lambda y: (g(y), h(y)))(f(x)) for x in mylist ]

This works because a "let" expression let x = e1 in e2 is equivalent to (lambda x: e2)(e1). I've had to do this occasionally when limited to only writing expressions (to be passed to eval), but it's not very readable, so I think the first solution is much better.

Upvotes: 1

chepner
chepner

Reputation: 531055

The correct use of the walrus operator to create 2-tuples would be

mylist = [(g(y:=f(x)), h(y)) for x in mylist]

which, yes, is even more horrendous than the 3-tuple version. If you want to forgo the walrus operator, use the version which := was supposed to obviate:

mylist = [(g(y), h(y)) for x in mylist for y in [f(x)]]

or more simply

mylist = [(g(y), h(y)) for y in map(f, mylist)]

I would not say the walrus operator is always so ungainly, but it seems to be more trouble than it is worth here.

Upvotes: 7

Woohoojin
Woohoojin

Reputation: 724

I would argue that map is more appropriate for applying an operation on every element of a list.

mylist = map(f, mylist)
mylist = list(map(lambda x: (g(x), g(x)), mylist))

Upvotes: 1

Related Questions