2015-08-24 12 views
5

Czasami niektóre wartości/ciągi są zakodowane na stałe w funkcjach. Na przykład w poniższej funkcji definiuję "stały" ciąg porównywania i sprawdzam przeciwko niemu.Zmienne kodowane w funkcji Pythona

def foo(s): 
    c_string = "hello" 
    if s == c_string: 
     return True 
    return False 

Bez dyskusji zbyt wiele o tym, dlaczego to jest złe, aby to zrobić, i jak to powinno być zdefiniowane w zakresie zewnętrznym, zastanawiam się, co dzieje się za kulisami, gdy jest zdefiniowane w ten sposób.
Czy ciąg jest tworzony dla każdego połączenia?
Jeśli zamiast ciągi "hello" była to lista: [1,2,3] (lub lista z zmienną zawartością, jeśli ma to znaczenie), czy to samo miało miejsce?

Odpowiedz

11

Ponieważ ciąg jest niezmienny (tak jak krotka), jest przechowywany z obiektem kodu bajtowego dla tej funkcji. Jest ładowany przez bardzo proste i szybkie wyszukiwanie indeksu. Jest to faktycznie szybsze niż wyszukiwanie globalne.

Można to zobaczyć w demontażu kodu bajtowego, za pomocą dis.dis() function:

>>> import dis 
>>> def foo(s): 
...  c_string = "hello" 
...  if s == c_string: 
...   return True 
...  return False 
... 
>>> dis.dis(foo) 
    2   0 LOAD_CONST    1 ('hello') 
       3 STORE_FAST    1 (c_string) 

    3   6 LOAD_FAST    0 (s) 
       9 LOAD_FAST    1 (c_string) 
      12 COMPARE_OP    2 (==) 
      15 POP_JUMP_IF_FALSE  22 

    4   18 LOAD_GLOBAL    0 (True) 
      21 RETURN_VALUE   

    5  >> 22 LOAD_GLOBAL    1 (False) 
      25 RETURN_VALUE   
>>> foo.__code__.co_consts 
(None, 'hello') 

LOAD_CONST opcode ładuje obiektu String z tablicy co_costs który jest częścią przedmiotu kod funkcji; odniesienie jest wypychane na wierzch stosu. Kod operacyjny STORE_FAST pobiera odniesienie od góry stosu i zapisuje je w tablicy miejscowej, ponownie bardzo prostą i szybką operację.

Do modyfikowalnych literałów ({..}, [..]) specjalne rozkazy zbudować obiekt, o zawartości nadal traktowane jako stałe jak to możliwe (bardziej złożonych struktur prostu wykonaj te same bloki):

>>> def bar(): return ['spam', 'eggs'] 
... 
>>> dis.dis(bar) 
    1   0 LOAD_CONST    1 ('spam') 
       3 LOAD_CONST    2 ('eggs') 
       6 BUILD_LIST    2 
       9 RETURN_VALUE   

BUILD_LIST call tworzy nowy obiekt listy, używając dwóch stałych obiektów napisowych.

Interesujący fakt: Jeśli użyłeś obiektu listy do testu członkostwa (something in ['option1', 'option2', 'option3'] Python wie, że obiekt listy nigdy nie zostanie zmutowany i przekonwertuje go na krotkę dla Ciebie podczas kompilacji (tak zwana optymalizacja peephole). to samo odnosi się do zestawu dosłownym, który jest przekształcany w frozenset() obiektu, ale tylko w Pythonie 3.2 i nowszym Zobacz Tuple or list when using 'in' in an 'if' clause?

pamiętać, że funkcja próbkę za pomocą wartości logicznych raczej --long; może po prostu wykorzystali.

def foo(s): 
    c_string = "hello" 
    return s == c_string 

dla dokładnie tego samego wyniku, unikając LOAD_GLOBAL wywołania w Python 2 (Python 3 wykonane True i False słów kluczowych, więc wartości mogą być również przechowywane jako stałe).

+0

Czy to samo nie dzieje się z 's ==" cześć "? – jonrsharpe

+0

Masz rację co do komentarza dotyczącego krotek, powinienem raczej zapytać o listę. Znowu poprawię pytanie. Reszta odpowiedzi na razie jest znakomita :) –

+0

@ArthurVaiselbuh: Ja również opisywałem listy, jak również słowniki i zestawy '{...}. –

Powiązane problemy