Odpowiadałem na to Pytanie , Preferowałem tutaj wyrażenie generatora i wykorzystałem to, co pomyślałem, że będzie szybszy, ponieważ generator nie musi najpierw tworzyć całej listy:

>>> lis=[['a','b','c'],['d','e','f']]
>>> 'd' in (y for x in lis for y in x)
True

I rozumieć listy Levon w jego Rozwiązanie,

>>> lis = [['a','b','c'],['d','e','f']]
>>> 'd' in [j for i in mylist for j in i]
True

Ale kiedy zrobiłem wyniki czasu dla tych LC było szybsze niż generator:

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f']]" "'d' in (y for x in lis for y in x)"
    100000 loops, best of 3: 2.36 usec per loop
~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f']]" "'d' in [y for x in lis for y in x]"
    100000 loops, best of 3: 1.51 usec per loop

Potem zwiększyłem rozmiar listy i ponownie go zmieniłem:

lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]

Tym razem wyszukiwania 'd' generator był szybszy niż LC, ale kiedy przeszukałem środkowego elementu (11), a ostatni element, a następnie LC ponownie uderzył wyrażenie generatora i nie mogę zrozumieć dlaczego?

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "'d' in (y for x in lis for y in x)"
    100000 loops, best of 3: 2.96 usec per loop

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "'d' in [y for x in lis for y in x]"
    100000 loops, best of 3: 7.4 usec per loop

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "11 in [y for x in lis for y in x]"
100000 loops, best of 3: 5.61 usec per loop

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "11 in (y for x in lis for y in x)"
100000 loops, best of 3: 9.76 usec per loop

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "18 in (y for x in lis for y in x)"
100000 loops, best of 3: 8.94 usec per loop

~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "18 in [y for x in lis for y in x]"
100000 loops, best of 3: 7.13 usec per loop
35
Ashwini Chaudhary 15 sierpień 2012, 08:29

3 odpowiedzi

Najlepsza odpowiedź

Rozszerzanie się na Odpowiedź Paulo, wyrażenia generatora są często wolniejsze niż wymienione kompensacje z powodu napowietrznych połączeń funkcyjnych. W tym przypadku zachowanie zwarcie in przesunięcia, że powolność, jeśli przedmiot zostanie znaleziony dość wcześnie, ale w przeciwnym razie wzór się trzyma.

Przeprowadziłem prosty skrypt przez profiler, aby uzyskać bardziej szczegółową analizę. Oto skrypt:

lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],
     [7,8,9],[10,11,12],[13,14,15],[16,17,18]]

def ge_d():
    return 'd' in (y for x in lis for y in x)
def lc_d():
    return 'd' in [y for x in lis for y in x]

def ge_11():
    return 11 in (y for x in lis for y in x)
def lc_11():
    return 11 in [y for x in lis for y in x]

def ge_18():
    return 18 in (y for x in lis for y in x)
def lc_18():
    return 18 in [y for x in lis for y in x]

for i in xrange(100000):
    ge_d()
    lc_d()
    ge_11()
    lc_11()
    ge_18()
    lc_18()

Oto odpowiednie wyniki, ponowne ustawienie, aby wzory były wyraźniejsze.

         5400002 function calls in 2.830 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   100000    0.158    0.000    0.251    0.000 fop.py:3(ge_d)
   500000    0.092    0.000    0.092    0.000 fop.py:4(<genexpr>)
   100000    0.285    0.000    0.285    0.000 fop.py:5(lc_d)

   100000    0.356    0.000    0.634    0.000 fop.py:8(ge_11)
  1800000    0.278    0.000    0.278    0.000 fop.py:9(<genexpr>)
   100000    0.333    0.000    0.333    0.000 fop.py:10(lc_11)

   100000    0.435    0.000    0.806    0.000 fop.py:13(ge_18)
  2500000    0.371    0.000    0.371    0.000 fop.py:14(<genexpr>)
   100000    0.344    0.000    0.344    0.000 fop.py:15(lc_18)

Tworzenie wyrażenia generatora jest odpowiednik Tworzenie funkcji generatora i dzwoniąc do niego . To konta na jeden telefon do <genexpr>. Następnie, w pierwszym przypadku next jest wywoływana 4 razy, aż do d zostanie osiągnięta, w sumie 5 połączeń (razy 100000 iterations = ncalls = 500000). W drugim przypadku nazywa się 17 razy, w sumie 18 połączeń; i w trzecim, 24 razy, w sumie 25 połączeń.

Genex przewyższa zrozumienie w pierwszym przypadku, ale dodatkowe połączenia do next na większość różnicy między prędkością rozumienia listy a prędkością wyrażenia generatora w drugiej i trzeciej przypadkach.

>>> .634 - .278 - .333
0.023
>>> .806 - .371 - .344
0.091

Nie jestem pewien, co odpowiada na pozostały czas; Wydaje się, że wyrażenia generatora byłyby wolniejsze włosy nawet bez dodatkowych połączeń funkcyjnych. Przypuszczam, że potwierdzam to Inspectorg4dget Asercja że "tworzenie rozumienia generatora ma bardziej rodzime koszty ogólne niż rozumienie listy". Ale w każdym przypadku pokazuje to jasno, że wyrażenia generatora są wolniejsze głównie z powodu połączeń do next.

Dodam, że gdy krótki obwód nie pomoże, kompleks listy są nadal szybciej, nawet dla bardzo dużych list. Na przykład:

>>> counter = itertools.count()
>>> lol = [[counter.next(), counter.next(), counter.next()] 
           for _ in range(1000000)]
>>> 2999999 in (i for sublist in lol for i in sublist)
True
>>> 3000000 in (i for sublist in lol for i in sublist)
False
>>> %timeit 2999999 in [i for sublist in lol for i in sublist]
1 loops, best of 3: 312 ms per loop
>>> %timeit 2999999 in (i for sublist in lol for i in sublist)
1 loops, best of 3: 351 ms per loop
>>> %timeit any([2999999 in sublist for sublist in lol])
10 loops, best of 3: 161 ms per loop
>>> %timeit any(2999999 in sublist for sublist in lol)
10 loops, best of 3: 163 ms per loop
>>> %timeit for i in [2999999 in sublist for sublist in lol]: pass
1 loops, best of 3: 171 ms per loop
>>> %timeit for i in (2999999 in sublist for sublist in lol): pass
1 loops, best of 3: 183 ms per loop

Jak widać, gdy zwarcie jest nieistotne, kompleksje listy są konsekwentnie Szybsze szybciej nawet na listę list. Oczywiście do rzeczywistych zastosowań in w tych skalach generatory będą szybsze z powodu zwarcia. Ale dla innych rodzajów zadań iteracyjnych, które są naprawdę liniowe w liczbie elementów, kompleksje listy są prawie zawsze zawsze szybciej. Jest to szczególnie prawdziwe, jeśli trzeba wykonać wiele testów na liście; Można zachować już ponad wybudowany rozumieć listy bardzo szybko :

>>> incache = [2999999 in sublist for sublist in lol]
>>> get_list = lambda: incache
>>> get_gen = lambda: (2999999 in sublist for sublist in lol)
>>> %timeit for i in get_list(): pass
100 loops, best of 3: 18.6 ms per loop
>>> %timeit for i in get_gen(): pass
1 loops, best of 3: 187 ms per loop

W tym przypadku rozumienie listy jest szybsze zamówienie wielkości!

Oczywiście pozostaje to tylko prawdziwe, dopóki nie skończysz z pamięci. Który przynosi mnie do mojego ostatniego punktu. Istnieją dwa główne powody, dla których warto użyć generatora: aby skorzystać z krótkiego sporta i zapisać pamięć. Dla bardzo dużych SEQENCS / Iterables generatory są oczywistym sposobem na wyjazd, ponieważ zapisują pamięć. Ale jeśli zwarcie nie jest opcją, dość dużo nigdy Wybierz generatory na listach dla prędkość . Wybrałeś je, aby zapisać pamięć i zawsze jest kompromis.

35
Community 23 maj 2017, 12:00

Całkowicie zależy od danych.

Generatory mają stały czas konfiguracji, który musi być amortyzowany, ile elementów jest nazywanych; Komplikacje listy są szybsze początkowo, ale wolne znacznie, ponieważ więcej pamięci jest używana z większych zestawów danych.

Przypomnijmy, że jako listy Cysthon są rozszerzone, lista jest zmieniana w wzorzeniu wzrostu 4, 8, 16, 25, 35, 46, 58, 72, 88, .... W przypadku większych kompensacji listy Python może przydzielić do 4x większej ilości pamięci niż rozmiar danych. Po trafieniu VM --- naprawdę Sloowww! Ale jak stwierdzono, wymienione kompensacje są szybsze niż generatory do małych zestawów danych.

Rozważ Case 1 , listę list 2x26:

LoL=[[c1,c2] for c1,c2 in zip(string.ascii_lowercase,string.ascii_uppercase)]  

def lc_d(item='d'):
    return item in [i for sub in LoL for i in sub]

def ge_d(item='d'):
    return item in (y for x in LoL for y in x)    

def any_lc_d(item='d'):
    return any(item in x for x in LoL)    

def any_gc_d(item='d'):
    return any([item in x for x in LoL])     

def lc_z(item='z'):
    return item in [i for sub in LoL for i in sub]

def ge_z(item='z'):
    return item in (y for x in LoL for y in x)    

def any_lc_z(item='z'):
    return any(item in x for x in LoL)    

def any_gc_z(item='z'):
    return any([item in x for x in LoL])               

cmpthese.cmpthese([lc_d,ge_d,any_gc_d,any_gc_z,any_lc_d,any_lc_z, lc_z, ge_z])   

Wyniki w tych czasach:

         rate/sec   ge_z   lc_z   lc_d any_lc_z any_gc_z any_gc_d   ge_d any_lc_d
ge_z      124,652     -- -10.1% -16.6%   -44.3%   -46.5%   -48.5% -76.9%   -80.7%
lc_z      138,678  11.3%     --  -7.2%   -38.0%   -40.4%   -42.7% -74.3%   -78.6%
lc_d      149,407  19.9%   7.7%     --   -33.3%   -35.8%   -38.2% -72.3%   -76.9%
any_lc_z  223,845  79.6%  61.4%  49.8%       --    -3.9%    -7.5% -58.5%   -65.4%
any_gc_z  232,847  86.8%  67.9%  55.8%     4.0%       --    -3.7% -56.9%   -64.0%
any_gc_d  241,890  94.1%  74.4%  61.9%     8.1%     3.9%       -- -55.2%   -62.6%
ge_d      539,654 332.9% 289.1% 261.2%   141.1%   131.8%   123.1%     --   -16.6%
any_lc_d  647,089 419.1% 366.6% 333.1%   189.1%   177.9%   167.5%  19.9%       --

Teraz rozważ Case 2 , który pokazuje szeroką dysformę między LC a Gen. W tym przypadku szukamy jednego elementu w liście listów 100 x 97 x 97 listy rodzaju struktury:

LoL=[[str(a),str(b),str(c)] 
       for a in range(100) for b in range(97) for c in range(97)]

def lc_10(item='10'):
    return item in [i for sub in LoL for i in sub]

def ge_10(item='10'):
    return item in (y for x in LoL for y in x)    

def any_lc_10(item='10'):
    return any([item in x for x in LoL])    

def any_gc_10(item='10'):
    return any(item in x for x in LoL)     

def lc_99(item='99'):
    return item in [i for sub in LoL for i in sub]

def ge_99(item='99'):
    return item in (y for x in LoL for y in x)    

def any_lc_99(item='99'):
    return any(item in x for x in LoL)    

def any_gc_99(item='99'):
    return any([item in x for x in LoL])      

cmpthese.cmpthese([lc_10,ge_10,any_lc_10,any_gc_10,lc_99,ge_99,any_lc_99,any_gc_99],c=10,micro=True)   

Wyniki tych czasów:

          rate/sec  usec/pass       ge_99      lc_99      lc_10  any_lc_99  any_gc_99  any_lc_10   ge_10 any_gc_10
ge_99            3 354545.903          --     -20.6%     -30.6%     -60.8%     -61.7%     -63.5% -100.0%   -100.0%
lc_99            4 281678.295       25.9%         --     -12.6%     -50.6%     -51.8%     -54.1% -100.0%   -100.0%
lc_10            4 246073.484       44.1%      14.5%         --     -43.5%     -44.8%     -47.4% -100.0%   -100.0%
any_lc_99        7 139067.292      154.9%     102.5%      76.9%         --      -2.4%      -7.0% -100.0%   -100.0%
any_gc_99        7 135748.100      161.2%     107.5%      81.3%       2.4%         --      -4.7% -100.0%   -100.0%
any_lc_10        8 129331.803      174.1%     117.8%      90.3%       7.5%       5.0%         -- -100.0%   -100.0%
ge_10      175,494      5.698  6221964.0% 4943182.0% 4318339.3% 2440446.0% 2382196.2% 2269594.1%      --    -38.5%
any_gc_10  285,327      3.505 10116044.9% 8036936.7% 7021036.1% 3967862.6% 3873157.1% 3690083.0%   62.6%        --

Jak widać - to zależy i jest to kompromis ...

13
the wolf 17 sierpień 2012, 14:45

W przeciwieństwie do popularnej wiary, kompymury listy są ładne dla umiarkowanych zakresów. Protokół Iterator implikuje połączenia dla iterator.__next__() oraz wywołania funkcji w Pythonie są - prawda, którą można powiedzieć - niewygodnie drogie.

Oczywiście w pewnym momencie Generatory Pamięć / CPU Skomponent rozpocznie się płacić, ale dla małych zestawów wymiarowych są bardzo wydajne.

10
Paulo Scardine 9 sierpień 2019, 17:31