2016-05-24 11 views
7

Czy poniższy fragment tworzy i niszczy listę stałych w każdej pętli, ponosząc cokolwiek (choć niewielkie) narzut, które to oznacza, lub czy lista jest tworzona raz?Czy stała lista używana jest w pętli skonstruowanej/usuniętej przy każdym przejściu?

for i in <some-type-of-iterable>: 
    if i in [1,3,5,18,3457,40567]: 
     print(i) 

Pytam zarówno o Python „standard”, tak jak istnieje jedna, a także o wspólnej realizacji CPython.
Mam świadomość, że ten przykład jest wymyślony, a także, że próba martwienia się o wydajność przy użyciu CPython jest głupia, ale jestem po prostu ciekawa.

+2

pytania takie są właśnie z tego powodu "dis"! –

+0

Odpowiedź wydaje się być "nie". – Jasper

Odpowiedz

2

Kolejny przykład z Pythonem 3.5, lista jest tworzona dla każdej iteracji.

>>> import dis 
>>> def func(): 
... for i in iterable: 
... for j in [1,2,3]: 
... print(i+j) 
... 
>>> dis.dis(func) 
    2   0 SETUP_LOOP    54 (to 57) 
       3 LOAD_GLOBAL    0 (iterable) 
       6 GET_ITER 
     >> 7 FOR_ITER    46 (to 56) 
      10 STORE_FAST    0 (i) 

    3   13 SETUP_LOOP    37 (to 53)  
      16 LOAD_CONST    1 (1)  # building list 
      19 LOAD_CONST    2 (2) 
      22 LOAD_CONST    3 (3) 
      25 BUILD_LIST    3 
      28 GET_ITER 
     >> 29 FOR_ITER    20 (to 52) # inner loop body begin 
      32 STORE_FAST    1 (j) 

    4   35 LOAD_GLOBAL    1 (print) 
      38 LOAD_FAST    0 (i) 
      41 LOAD_FAST    1 (j) 
      44 BINARY_ADD 
      45 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      48 POP_TOP 
      49 JUMP_ABSOLUTE   29   # inner loop body end 
     >> 52 POP_BLOCK 
     >> 53 JUMP_ABSOLUTE   7   # outer loop end, 
               # jumping back before list creation 
     >> 56 POP_BLOCK 
     >> 57 LOAD_CONST    0 (None) 
      60 RETURN_VALUE 
+0

Którą listę mówisz, że zostanie utworzona tylko raz? Nie jestem ekspertem od czytania kodu bajtowego Pythona, ale wygląda na to, że wewnętrzna lista ('[4,5,6]') zostanie zbudowana 3 razy (raz dla każdego 'a') z powodu' JUMP_ABSOLUTE 16' który odsyła cię przed drugim 'SETUP_LOOP',' BUILD_LIST', etc ... – mgilson

+0

Masz rację. Naprawiono i zaktualizowano przykład. – Jasper

+0

Założę się, że python2.7 również miałby tę samą wydajność ... chociaż nie jestem pewien, dlaczego nie optymalizują tej BUILD_LIST. Nie mogę wymyślić żadnego sposobu, aby uzyskać odniesienie do tej listy - przypuszczam, że pętla z "stałą" listą do iteracji jest rzadkim przypadkiem, że została przeoczona? – mgilson

3

To zależy od implementacji i wersji Pythona oraz od sposobu użycia "stałych list". Na Cpython2.7.10 ze swoim przykładzie, wygląda na to, że odpowiedź jest, że lista w stan rachunku if jest tworzony tylko raz ...

>>> def foo(): 
... for i in iterable: 
...  if i in [1, 3, 5]: 
...  print(i) 
... 
>>> import dis 
>>> dis.dis(foo) 
    2   0 SETUP_LOOP    34 (to 37) 
       3 LOAD_GLOBAL    0 (iterable) 
       6 GET_ITER    
     >> 7 FOR_ITER    26 (to 36) 
      10 STORE_FAST    0 (i) 

    3   13 LOAD_FAST    0 (i) 
      16 LOAD_CONST    4 ((1, 3, 5)) 
      19 COMPARE_OP    6 (in) 
      22 POP_JUMP_IF_FALSE  7 

    4   25 LOAD_FAST    0 (i) 
      28 PRINT_ITEM   
      29 PRINT_NEWLINE  
      30 JUMP_ABSOLUTE   7 
      33 JUMP_ABSOLUTE   7 
     >> 36 POP_BLOCK   
     >> 37 LOAD_CONST    0 (None) 
      40 RETURN_VALUE   

Wskazówka: 16 LOAD_CONST 4 ((1, 3, 5))

Pythona wizjer optymalizator okazało nasza lista do krotki (dzięki python!) i zapisana jako stała. Zauważ, że optymalizator peephole może wykonać te transformacje tylko na obiektach, jeśli wie, że ty jako programista absolutnie nie masz możliwości uzyskania odnośnika do listy (w przeciwnym razie możesz zmutować listę i zmienić znaczenie kodu). O ile mi wiadomo, wykonują tę optymalizację tylko dla literałów, które składają się ze stałych i są RHS operatora in. Mogą istnieć inne przypadki, o których nie wiem (dis.dis jest Twoim przyjacielem w znajdowaniu tych optymalizacji).

Dowiedziałem się o tym wyżej, ale możesz zrobić to samo z set-literals w nowszych wersjach pythona (w python3.2 +, set jest konwertowany na stałą frozenset). Korzyścią jest to, że set/frozenset mają szybsze testy członkostwa średnio niż list/tuple.

Powiązane problemy