1
\$\begingroup\$

I'm relatively new to Python and learned, that function cannot return nothing, not even None. In my application I want to iterate over a list and keep all non-None results of a complicated function:

def complicated_function(x):
    if x < 0:
        return "a"
    elif x == 3.6:
        return "b"
    elif x == 4:
        return None  # Idealy here nothing should be returned
    elif x > 10:
        return "c"
    else:
        return "d"

I have three soltuions and my question is which would you recommend me to use and are there better ones?

First solution

tmp = [complicated_function(x) for x in [-1, 2, 3.6, 4, 5, 20]]
ret = [x for x in tmp if x]

Second solution

ret = [complicated_function(x) for x in [-1, 2, 3.6, 4, 5, 20] if complicated_function(x)]

Third solution

ret = []
for x in [-1, 2, 3.6, 4, 5, 20]:
    tmp = complicated_function(x)
    if tmp:
        ret.append(tmp)

I think the first solution is slow, if the list to iterate over is very large. The second solution seems to be bad if the complated_function is really expensive (in rumtime). The third solution seems to be ok, but append is said to be significantly slower than list comprehension.

\$\endgroup\$
  • 3
    \$\begingroup\$ Which Python version are you using? In Python 3.8+ you can make your list comprehension less expensive by calling the function only once using the walrus operator. \$\endgroup\$ – Graipher Mar 20 at 11:13
  • \$\begingroup\$ @Graipher How would the walrus operator work? \$\endgroup\$ – Qaswed Mar 20 at 14:36
  • 5
    \$\begingroup\$ ret = [y for x in [-1, 2, 3.6, 4, 5, 20] if (y := complicated_function(x))] \$\endgroup\$ – Graipher Mar 20 at 14:45
  • \$\begingroup\$ @Graipher Thanks for your explanaition of the walrus operator. I'm using Python 3.7, but definitely will keep this in mind as an option when switching to 3.8. \$\endgroup\$ – Qaswed Mar 20 at 15:00
  • 2
    \$\begingroup\$ stackoverflow.com/a/48609910/7311767 \$\endgroup\$ – Stephen Rauch Mar 20 at 15:26
5
\$\begingroup\$

You can use an intermediate generator function:

[y for y in (complicated_function(x) for x in <my_iterable>) if y is not None]

An alternative is changing complicated_function to become a generator that accepts an iterable, instead of a pure function:

def complicated_function(iterable):
    for x in iterable:
        if x < 0:
            yield "a"
        elif x == 3.6:
            yield "b"
        elif x == 4:
            continue
        elif x > 10:
            yield "c"
        else:
            yield "d"

and then: list(complicated_function(<my_iterable>))

| improve this answer | |
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.