2015-08-24 6 views
5

A volte, alcuni valori/stringhe sono codificati nelle funzioni. Ad esempio, nella seguente funzione, definisco una stringa di confronto "costante" e la confronto con essa.Variabili hard coded in funzione python

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

Senza discutere troppo su questo che è male per fare questo, e come dovrebbe essere definito nel perimetro esterno, mi chiedo che cosa succede dietro le quinte quando è definito in questo modo.
La stringa viene creata per ogni chiamata?
Se al posto della stringa "hello" fosse la lista: [1,2,3] (o una lista con contenuto mutevole se fosse importante) sarebbe la stessa cosa?

risposta

11

Poiché la stringa è immutabile (come una tupla), viene memorizzata con l'oggetto bytecode per la funzione. È caricato da una ricerca indice molto semplice e veloce. Questo è in realtà più veloce di una ricerca globale.

Si può vedere questo in un disassemblaggio del bytecode, utilizzando il 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') 

Il LOAD_CONST opcode carica l'oggetto stringa dalla matrice co_costs che fa parte dell'oggetto codice per la funzione; il riferimento è spinto in cima allo stack. L'opcode STORE_FAST prende il riferimento dalla cima dello stack e lo memorizza nell'array locale, di nuovo un'operazione molto semplice e veloce.

Per letterali mutabili ({..}, [..]) opcode speciali costruire l'oggetto, con il contenuto ancora trattati come costanti il ​​più possibile (strutture più complesse basta seguire le stesse particelle elementari):

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

Il BUILD_LIST call crea il nuovo oggetto lista, usando due oggetti stringa costanti.

Fatto interessante: se si utilizza un oggetto elenco per un test di appartenenza (something in ['option1', 'option2', 'option3'] Python sa che l'oggetto elenco non sarà mai mutato e lo convertirà in una tupla per te in fase di compilazione (una cosiddetta ottimizzazione spioncino). stesso vale per una serie letterale, che viene convertito in un oggetto frozenset(), ma solo in Python 3.2 e successivi Vedi Tuple or list when using 'in' in an 'if' clause?

Nota che la funzione di esempio sta usando booleani piuttosto verboso, si potrebbe semplicemente aver usato:.

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

per lo stesso risultato esatto, evitando lo LOAD_GLOBAL chiamate in Python 2 (Python 3 ha creato True e False parole chiave in modo che i valori possano anche essere memorizzati come costanti).

+0

Non succede la stessa cosa con 's ==" ciao "'? – jonrsharpe

+0

Hai ragione riguardo al commento sulle tuple, avrei dovuto chiederti invece dell'elenco. Ill correggere la domanda. Il resto della risposta è eccellente per ora :) –

+0

@ArthurVaiselbuh: Ho anche elencato gli elenchi ora, oltre a dizionari e set di '{...}'. –