Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing multiple calls in list comprehension [duplicate]

Using the below as an example, we can see x.giveMyNum() will be called 4 times - 3 times to check the value of myNum and once to construct the list to return. You'd probably want it to be called only 3 times, as it's a pure function and its value won't change.

List Comprehension Version:

class test(object):
    def __init__(self,myNum):
        self.myNum=myNum
    def giveMyNum(self):
        print "giving"
        return self.myNum
q=[test(x) for x in range(3)]
print [x.giveMyNum() for x in q if x.giveMyNum()>1]

I know you can do something like this to fix it:

ret=[]
for x in q:
    k=x.giveMyNum()
    if k>1:
        ret.append(k)

but is there a way to prevent the extra call in a list comprehension?

I don't need to keep the intermediate value.

like image 219
Carbon Avatar asked Jul 15 '15 22:07

Carbon


1 Answers

You can combine it with a generator but I would stick with a regular loop.

print([n for n in (x.giveMyNum() for x in q) if n  > 1 ])

If you like functional code you could map or itertools.imap with python2:

print([n for n in map(test.giveMyNum, q) if n > 1 ])

Using python2 and imap is faster than the gen exp:

In [8]: q = [test(x) for x in range(10000)]

In [9]: timeit [ n for n in imap(test.giveMyNum, q) if n > 1]
1000 loops, best of 3: 1.94 ms per loop

In [10]: timeit [n for n in (x.giveMyNum() for x in q) if n  > 1 ]
100 loops, best of 3: 2.56 ms per loop

map is also faster using python3:

In [2]: timeit [ n for n in map(test.giveMyNum, q) if n > 1]
100 loops, best of 3: 2.23 ms per loop

In [3]: timeit [n for n in (x.giveMyNum() for x in q) if n  > 1 ]
100 loops, best of 3: 2.93 ms per loop

The timing for the regular loop and calling the method:

In [8]: timeit [x.giveMyNum() for x in q if x.giveMyNum()>1]
100 loops, best of 3: 3.59 ms per loop

In [9]: %%timeit
ret=[]
for x in q:
    k=x.giveMyNum()
    if k>1:
        ret.append(k)
   ...: 
100 loops, best of 3: 2.67 ms per loop

Python3:

In [2]: %%timeit
ret=[]
for x in q:
    k=x.giveMyNum()
    if k>1:
        ret.append(k)
   ...: 
100 loops, best of 3: 2.84 ms per loop

In [3]: timeit [x.giveMyNum() for x in q if x.giveMyNum()>1]

100 loops, best of 3: 4.08 ms per loop
like image 149
Padraic Cunningham Avatar answered Sep 28 '22 04:09

Padraic Cunningham