1

I have a function which has a for loop like this

def my_func():
    order = [1,2,3,4,5,6,7]
    my_list = []
    for o in order:
        tmp = is_odd(o)
        if tmp:
            my_list.append(tmp)
    return my_list

def is_odd(n):
    if n%2 != 0:
        return n
    else:
        return False

I want to convert it into one line python for loop. I did it like this

def my_func():
    order = [1,2,3,4,5,6,7]
    my_list = [is_odd(o) for o in order if is_odd(o)]
    return my_list

def is_odd(n):
    if n%2 != 0:
        return n
    else:
        return False

The problem with this code is that it call is_odd function twice in each loop iteration. Is there any other way to convert loop into one line loop and calling is_odd function only once?

(This is not original code. Just an example)

Muhammad Hassan
  • 12,455
  • 6
  • 28
  • 50
  • If you only use is_odd for that just make it return only True or False, if not just create a new function that do it. You know you can do `[o for o in order if is_odd(o) ]`? – polku May 18 '16 at 11:18
  • It does not return just false or true. It return an object in an array like `[object]` if it find it. Otherwise it return `None`. As I mention, it is not my original code. It is just an example. – Muhammad Hassan May 18 '16 at 11:24
  • So you show code completely different from yours and expect good answers, ... ok. – polku May 18 '16 at 11:32
  • It is not different from my code. I showed in my code that I want to use value returned by `is_odd` function for `if` condition as well as in my list. – Muhammad Hassan May 18 '16 at 11:42

3 Answers3

4

As described in the question's answers that I've linked as duplicate to your question:

You'd create a generator (or a list) that produces the (intermediate) results of your function's calls and produce the filtered output based on that:

Example:

def my_func():
    order = [1,2,3,4,5,6,7]
    intermediate_results = (is_odd(o) for o in order)
    # alternatively filter using `filter(None, intermediate_results)`
    return [i for i in intermediate_results if i]
Community
  • 1
  • 1
moooeeeep
  • 28,315
  • 14
  • 88
  • 166
  • Can you please tell me how I can give arguments to `foo` function in `itertools.ifilter(None, itertools.imap(foo, range(10)))` as in my case, it takes 2 arguments. This solution is from link you mentioned. – Muhammad Hassan May 18 '16 at 11:45
  • @MHassan It depends. Is it a constant? Is it a different value for each item in your input list? After all it seems to be a different question. Try to find out how to pass additional arguments to the `map()` function's function argument. If you can't find it out yourself, consider posting a new question. – moooeeeep May 18 '16 at 11:50
  • Thanx for help. I use intermediate_result like you and it works. – Muhammad Hassan May 18 '16 at 11:53
  • 1
    @MHassan Have a look at this: http://stackoverflow.com/q/10834960/1025391 – moooeeeep May 18 '16 at 11:55
2

In this case, you can use the filter function. E.g. my_list = filter(is_odd, order).

If you have similar needs, look at the itertools module.

  • 1
    This doesn't work, because OP needs the truthy return values of the function passed, not the items for which the function returns a truthy value. (This suggestion achieves the latter.) – moooeeeep May 18 '16 at 11:42
1

Hi – it looks like you return the function is_odd(o) (thus run it again) for the instances of o where the function is_odd(o) returns true. Following should only run once:

def my_func():
    order = [1,2,3,4,5,6,7]
    my_list = [o for o in order if is_odd(o)]
    return my_list
c0derabbit
  • 33
  • 7
  • The list comprehension is equivalent to the built-in `filter()`, which I would thus recommend to use instead. – Eric O Lebigot May 18 '16 at 11:26
  • As I mentioned in my question, it is not original code. I want to append value return by `is_odd(o)` function to my list if `is_odd(o)` is not false otherwise do not append any thing. – Muhammad Hassan May 18 '16 at 11:27
  • Agreed – short & sweet, `filter()` is the most elegant solution. Though I wanted to explain why the function runs twice and how to prevent this. – c0derabbit May 18 '16 at 11:30
  • @MHassan, all of the above answers make this possible if you adapt them to your different code. You could also define the func with an argument: `def my_func(arg): ...` and then run `my_func([1, 2, 3, 4, 5, 6, 7])` or `my_func(my_list)` – c0derabbit May 18 '16 at 11:38
  • Ok. Thanx. I am using `itertools` to solve my problem. – Muhammad Hassan May 18 '16 at 11:41
  • 1
    This doesn't work, because OP needs the truthy return values of the function passed, not the items for which the function returns a truthy value. (This suggestion achieves the latter.) – moooeeeep May 18 '16 at 11:43