2015-07-15 2 views
5

Используя приведенное ниже, мы можем видеть, что x.giveMyNum() будет вызываться 4 раза - 3 раза, чтобы проверить значение myNum и один раз, чтобы построить возвращаемый список. Вероятно, вы хотите, чтобы его вызывали только 3 раза, так как это чистая функция, и ее значение не изменится.Предотвращение множественных вызовов в понимании списка

Список Постижение Версия:

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] 

Я знаю, что вы можете сделать что-то вроде этого, чтобы исправить это:

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

но есть способ, чтобы предотвратить дополнительный вызов в списке понимание?

Мне не нужно сохранять промежуточное значение.

ответ

3

Вы можете комбинировать его с генератором, но я бы придерживался регулярной петли.

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

Если вы хотите функциональный код можно отобразить или itertools.imap с python2:

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

Использование python2 и IMAP быстрее, чем ехр поколения:

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 

карта также быстрее, используя 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 

Время для регулярного цикла и вызова метода:

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 
+0

Мне нравится функциональный код. Это очень читаемо. – Carbon

+0

@ProbablyAStupidQuestion, тогда вам может понравиться 'filter (lambda n: n> 1, map (test.giveMyNum, q)))';) –

+0

, что очень приятно! Я грустный фильтр перемещается в itertools в Python3. – Carbon

3

Это возможно, но не очень читаемый ...

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

так, если giveMyNum() не является медленным или не чистым, я бы избегал этого.

+0

Это потрясающе - понимание генератора! Это действительно умно. Я спрашивал, потому что у меня случай с медленной чистой функцией, и это решает это красиво. – Carbon