2009-11-09 3 views
37

Penso che mettere la dichiarazione di importazione il più vicino possibile al frammento che la utilizza facilita la leggibilità rendendo più chiare le sue dipendenze. Python lo metterà in cache? Dovrei preoccuparmi? È una cattiva idea?Istruzioni di importazione locali in Python

def Process(): 
    import StringIO 
    file_handle=StringIO.StringIO('hello world') 
    #do more stuff 

for i in xrange(10): Process() 

Un po 'più giustificazioni: è per i metodi che utilizzano i bit arcani della biblioteca, ma quando ho refactoring il metodo in un altro file, non si rendono conto che ho perso la dipendenza esterna fino a ottenere un errore di runtime.

+0

sarebbe davvero bello avere un "con (importare StringIO) come moduleName: syntax – JeremyKun

+0

@Bean: se è così che non devi digitare un nome di modulo lungo o scomodo, allora c'è un modo semplice:' importare StringIO' e poi 'sio = StringIO'. Ora puoi fare' file_handle = sio.StringIO ('ciao mondo') 'e salvare quei preziosi cinque caratteri. Lo userei con parsimonia, però, perché può rendere il codice più difficile leggere (l'assegnazione è facile da perdere, i nomi dei moduli non standard possono distrarre.) – cvoinescu

risposta

63

Le altre risposte mostrano una lieve confusione su come funziona davvero import.

Questa dichiarazione:

import foo 

equivale all'incirca a questa dichiarazione:

foo = __import__('foo', globals(), locals(), [], -1) 

cioè, crea una variabile nell'ambito corrente omonimo del modulo richiesto, e cessionari è il risultato della chiamata di __import__() con il nome di quel modulo e un carico di argomenti predefinito.

La funzione __import__() gestisce concettualmente una stringa ('foo') in un oggetto modulo. I moduli sono memorizzati nella cache sys.modules, e questo è il primo aspetto __import__() - se sys.modules ha una voce per 'foo', questo è ciò che restituirà __import__('foo'), qualunque esso sia. Davvero non interessa il tipo.Puoi vederlo in azione tu stesso; provare a eseguire il seguente codice:

import sys 
sys.modules['boop'] = (1, 2, 3) 
import boop 
print boop 

Lasciando da parte le preoccupazioni stilistiche, per il momento, avere un'istruzione import all'interno di una funzione funziona come si vorrebbe. Se il modulo non è mai stato importato prima, viene importato e memorizzato nella cache in sys.modules. Quindi assegna il modulo alla variabile locale con quel nome. Lo standard non non è modificare qualsiasi stato a livello di modulo. È eventualmente modificare alcuni stato globale (aggiungendo una nuova voce a sys.modules).

Detto questo, non utilizzo quasi mai import all'interno di una funzione. Se l'importazione del modulo crea un rallentamento notevole nel tuo programma, come se eseguisse un lungo calcolo nella sua inizializzazione statica, o semplicemente un enorme modulo, e il tuo programma raramente ha effettivamente bisogno del modulo per qualsiasi cosa, è perfettamente bene avere l'importazione solo all'interno le funzioni in cui è usato. (Se questo è stato spiacevole, Guido saltava nella sua macchina del tempo e cambiava Python per impedirci di farlo.) Ma di regola io e la comunità Python generale mettevamo tutte le nostre istruzioni di importazione nella parte superiore del modulo nell'ambito del modulo.

+15

occasionalmente ti salva anche dalle importazioni cicliche (ad esempio: se devi importare un modello nel tuo file manager.py con django e model.py importa già il file manager.py ... cosa che generalmente fa) – Jiaaro

+4

la risposta chiara, sono felice che tu abbia usato 3 _non_, perché 2 o 4 sarebbero confusi. – matiasg

10

Vedere PEP 8:

Le importazioni sono sempre messo in cima file, subito dopo qualsiasi modulo commenti e docstrings, e prima di variabili globali del modulo e costanti.

prega di notare che questo è puramente una scelta stilistica come Python tratterà tutti import dichiarazioni lo stesso indipendentemente da dove essi sono dichiarati nel file di origine. Tuttavia, ti consiglio di seguire una pratica comune in quanto renderà il tuo codice più leggibile agli altri.

+2

collegamento interessante, ma: "Due buoni motivi per violare una regola particolare: (1) Quando si applica la regola renderebbe il codice meno leggibile, anche per qualcuno che è abituato a leggere il codice che segue le regole. ... " –

+0

Questo vale solo per le importazioni di livello superiore. Un'importazione all'interno di una funzione non verrà elaborata fino a quando non viene invocata quella funzione. Anche se generalmente sconsiglio di farlo, può essere utile quando si ha una dipendenza che è o molto costoso da caricare o potrebbe non essere disponibile in tutti gli ambienti. –

9

Stile a parte, è vero che un modulo importato verrà importato solo una volta (a meno che reload non venga chiamato su tale modulo). Tuttavia, ogni chiamata a import Foo verificherà implicitamente se il modulo è già caricato (controllando sys.modules).

considerare anche la "smontaggio" di due funzioni altrimenti uguali in cui si cerca di importare un modulo e l'altro no:

>>> def Foo(): 
...  import random 
...  return random.randint(1,100) 
... 
>>> dis.dis(Foo) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (random) 
       9 STORE_FAST    0 (random) 

    3   12 LOAD_FAST    0 (random) 
      15 LOAD_ATTR    1 (randint) 
      18 LOAD_CONST    2 (1) 
      21 LOAD_CONST    3 (100) 
      24 CALL_FUNCTION   2 
      27 RETURN_VALUE   
>>> def Bar(): 
...  return random.randint(1,100) 
... 
>>> dis.dis(Bar) 
    2   0 LOAD_GLOBAL    0 (random) 
       3 LOAD_ATTR    1 (randint) 
       6 LOAD_CONST    1 (1) 
       9 LOAD_CONST    2 (100) 
      12 CALL_FUNCTION   2 
      15 RETURN_VALUE   

Non sono sicuro di quanto più il bytecode viene tradotta per la macchina virtuale, ma se questo fosse un importante ciclo interno al tuo programma, vorresti sicuramente mettere un po 'di peso sull'approccio Bar sull'approccio Foo.

Un rapido e sporco timeit prova non mostra un miglioramento della velocità modesta quando si utilizza Bar:

$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Foo()" 
200000 loops, best of 3: 10.3 usec per loop 
$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Bar()" 
200000 loops, best of 3: 6.45 usec per loop 
3

Quando l'interprete Python colpisce un'istruzione import, si comincia a leggere tutte le definizioni di funzioni del file che viene importato . Questo spiega perché a volte le importazioni possono richiedere del tempo.

L'idea alla base di tutte le importazioni all'inizio è una convenzione stilistica come sottolinea Andrew Hare. Tuttavia, devi tenere presente che così facendo, stai facendo in modo che l'interprete verifichi se questo file è già stato importato dopo la prima volta che lo importi. Diventa anche un problema quando il tuo file di codice diventa grande e vuoi "aggiornare" il tuo codice per rimuovere o sostituire certe dipendenze. Ciò richiederà la ricerca dell'intero file di codice per trovare tutti i luoghi in cui è stato importato questo modulo.

Suggerirei seguendo la convenzione e mantenendo le importazioni nella parte superiore del file di codice. Se vuoi davvero tenere traccia delle dipendenze per le funzioni, ti suggerisco di aggiungerle nello docstring per quella funzione.

+1

"Quando l'interprete python raggiunge un'istruzione import, inizia a leggere tutte le definizioni di funzione nel file" - (1) accade solo la PRIMA volta che il modulo viene importato durante l'esecuzione corrente dell'interprete Python (e il modulo importato è nascosto in sys.modules in modo che la prossima volta sia richiesta solo una ricerca del nome (2) "leggere le definizioni delle funzioni" NON è quello che fa; ESEGUI tutti i moduli -level (per lo più 'def' e 'class') .Altre cose, ad esempio più importazioni, l'impostazione di strutture dati a livello di modulo (a volte da file) può richiedere un po '. (3) irrilevante. –

0

posso vedere due modi in cui è necessario importare localmente

  1. fini di prova o per un utilizzo temporaneo, è necessario importare qualcosa, in questo caso si dovrebbe mettere l'importazione nel luogo di utilizzo.

  2. A volte per evitare la dipendenza ciclica è necessario importarlo all'interno di una funzione, ma ciò significherebbe che si è verificato un problema in altri casi.

Altrimenti posizionarlo sempre in alto per efficienza e coerenza.

8

Ho fatto questo, e poi ho desiderato di no. Normalmente, se sto scrivendo una funzione, e quella funzione deve usare StringIO, posso guardare la parte superiore del modulo, vedere se viene importata, e poi aggiungerla se non lo è.

Supponiamo che io non faccia questo; supponiamo di aggiungerlo localmente all'interno della mia funzione. E poi supponiamo che a qualcuno il punto I, o qualcun altro, aggiunga una serie di altre funzioni che usano StringIO. Quella persona guarderà la parte superiore del modulo e aggiungerà import StringIO. Ora la tua funzione contiene codice non solo inaspettato ma ridondante.

Inoltre, viola quello che secondo me è un principio piuttosto importante: non modificare direttamente lo stato a livello di modulo all'interno di una funzione.

Edit:

In realtà, si scopre che tutto quanto sopra è una sciocchezza.

L'importazione di un modulo non modifica lo stato a livello di modulo (inizializza il modulo che viene importato, se non altro ancora, ma non è affatto la stessa cosa). L'importazione di un modulo che hai già importato altrove non ti costa nulla se non una ricerca su sys.modules e la creazione di una variabile nell'ambito locale.

Conoscendo questo, mi sento un po 'stupido a sistemare tutti i punti del mio codice in cui l'ho risolto, ma questa è la mia croce da sopportare.

+1

uso non ci avevo pensato ed è davvero ovvio in retrospettiva. –

+0

Se con questo si intende "import StringIO" nel mezzo di una funzione, cambia qualcosa per lo scope del modulo in cui è stata definita la funzione ... questo è sbagliato. Come ho spiegato nella mia risposta, l'esecuzione di una "importazione" nel mezzo di una funzione non cambia nulla nello stato del modulo in cui tale funzione è stata definita. E cosa c'è di sbagliato nella modifica dello stato a livello di modulo dall'interno di una funzione? C'è un'intera parola chiave dedicata a fare proprio quella cosa: "globale". –

+0

+1 per la franchezza. –