Perché "import *" è male?


153

Si consiglia di non utilizzare import *in Python.

Qualcuno può condividere il motivo di ciò, in modo da poterlo evitare la prossima volta?



2
dipende se stai scrivendo o scrivendo codice che devi riutilizzare. a volte paga ignorare gli standard di codice. "import *" può anche andare bene se hai una convenzione di denominazione che chiarisce da dove provengono le cose. ad es. "da Cats import *; TabbyCat; MaineCoonCat; CalicoCat;"
gatoatigrado,

3
import *non funziona per me in primo luogo in Python 2 o 3.
joshreesjones,

1
Questo risponde alla tua domanda? Cosa importa esattamente "import *"?
AMC

Risposte:


223
  • Perché mette molte cose nel tuo spazio dei nomi (potrebbe oscurare qualche altro oggetto dalla precedente importazione e non lo saprai).

  • Perché non sai esattamente cosa viene importato e non puoi facilmente trovare da quale modulo è stata importata una certa cosa (leggibilità).

  • Perché non puoi usare strumenti interessanti come pyflakesrilevare staticamente errori nel tuo codice.


2
Sì, odio davvero il mio lavoro quando qualcuno usa * import, perché non posso semplicemente eseguire i pyflakes ed essere felice, ma devo riparare quelle importazioni. È bello però, che con quei pyflakes mi aiuti a :-)
gruszczy

7
Come esempio concreto, molti utenti di NumPy sono stati morsi numpy.anydall'ombra anyquando lo fanno from numpy import *o uno strumento "utile" lo fa per loro.
user2357112 supporta Monica

1
Dovrei evitare di usare l'opzione --pylab per IPython per gli stessi motivi?
timgeb,

6
Per evidenziare un rischio a cui non avevo mai pensato prima di leggere questo ("potrebbe oscurare qualche altro oggetto dall'importazione precedente"): import *rende significativo l' ordine delle importistruzioni ... anche per i moduli di libreria standard che normalmente non si preoccupano dell'ordine di importazione . Qualcosa di innocente come l'alfabetizzazione delle tue importdichiarazioni potrebbe rompere la tua sceneggiatura quando un'ex vittima della guerra di importazione diventa l'unico sopravvissuto. (Anche se il tuo script funziona ora e non cambia mai, potrebbe improvvisamente fallire qualche tempo dopo se il modulo importato introduce un nuovo nome che sostituisce quello su cui stavi facendo affidamento.)
Kevin J. Chase,

49

Secondo lo Zen di Python :

Esplicito è meglio che implicito.

... non posso discuterne, sicuramente?


29
In realtà, puoi discuterne. È anche del tutto incoerente, dato che non si dichiarano esplicitamente le variabili in Python, queste vengono semplicemente visualizzate dopo l'assegnazione.
Konrad Rudolph,

7
@gruszczy: dichiarare le variabili è ridondante a cosa ? Assegnazione? No, sono due concetti separati e dichiarare qualcosa trasmette un'informazione molto distinta e importante. Ad ogni modo, la esplicitazione è sempre in qualche modo legata alla ridondanza, sono due facce della stessa medaglia.
Konrad Rudolph,

3
@kriss giusto, ma non era quello il mio punto. Il mio punto era che la mancata dichiarazione esplicita di una variabile porta a errori. Dici che "l'assegnazione senza [dichiarazione] è impossibile". Ma è sbagliato, il mio punto è che Python purtroppo rende esattamente questo possibile.
Konrad Rudolph,

3
@kriss Un'altra informazione fornita al compilatore dalla dichiarazione è il fatto che si intende dichiarare una nuova variabile. Questa è un'informazione cruciale per il sistema di tipi. Dici che gli IDE moderni risolvono il problema di errori di digitazione ma questo è semplicemente sbagliato, e in effetti questo è un problema sostanziale nei linguaggi non compilati staticamente, motivo per cui Perl ha aggiunto use strict(JavaScript var). A parte questo, ovviamente Python non è senza tipo (in realtà è fortemente tipizzato). Comunque, anche se avessi ragione, ciò contraddirebbe comunque lo Zen di Python, citato in questa risposta.
Konrad Rudolph,

3
@kriss Hai sbagliato: riutilizzare lo stesso nome di variabile non è un problema - riutilizzare la stessa variabile è (cioè lo stesso nome nello stesso ambito). Dichiarazioni esplicite impedirebbero esattamente questo errore (e altri, basati sul semplice errore di digitazione, che, come ho già detto, è in realtà un problema estremamente comune e dispendioso in termini di tempo, anche se hai ragione che il problema è più grande in Perl-like le lingue). E la contraddizione a cui alludo è il requisito dello Zen di esplicitare, che viene espulso dal finestrino qui.
Konrad Rudolph,

40

Non passi **locals()alle funzioni, vero?

Poiché Python manca di un'istruzione "include" e il selfparametro è esplicito e le regole di scoping sono piuttosto semplici, di solito è molto facile puntare un dito su una variabile e dire da dove proviene quell'oggetto - senza leggere altri moduli e senza alcun tipo di IDE (che sono comunque limitati in termini di introspezione, dal fatto che il linguaggio è molto dinamico).

Tutto ciò import *rompe.

Inoltre, ha una concreta possibilità di nascondere bug.

import os, sys, foo, sqlalchemy, mystuff
from bar import *

Ora, se il modulo bar ha uno degli attributi " os", " mystuff", ecc ..., sovrascriveranno quelli esplicitamente importati e probabilmente indicheranno cose molto diverse. Definire __all__nel bar è spesso saggio - questo afferma ciò che implicitamente essere importati - ma ancora è difficile rintracciare in cui gli oggetti provengono, senza leggere e analisi del modulo di bar e seguendo le sue importazioni. Una rete di import *è la prima cosa che risolvo quando divento la proprietà di un progetto.

Non fraintendermi: se import *mancassero, piangerei per averlo. Ma deve essere usato con cura. Un buon caso d'uso è fornire un'interfaccia di facciata su un altro modulo. Allo stesso modo, l'uso di istruzioni di importazione condizionali o importazioni all'interno di spazi dei nomi di funzioni / classi richiede un po 'di disciplina.

Penso che in progetti medio-grandi, o piccoli con diversi partecipanti, sia necessario un minimo di igiene in termini di analisi statica - eseguendo almeno pyflakes o ancora meglio un pilastro correttamente configurato - per catturare diversi tipi di bug prima accadono.

Ovviamente dato che si tratta di Python - sentiti libero di infrangere le regole e di esplorare - ma fai attenzione ai progetti che potrebbero crescere di dieci volte, se al codice sorgente manca la disciplina sarà un problema.


6
Python 2.x fa avere una dichiarazione "include". Si chiama execfile(). Fortunatamente, è raramente usato e andato in 3.x.
Sven Marnach,

Che ne dici **vars()di includere globali se la funzione chiamata si trova in un altro file? : P
Solomon Ucko,

16

Va bene farlo from ... import *in una sessione interattiva.


Che ne dici di una docteststringa? Fa il import *get interpretato all'interno di una "sandbox" in questo caso? Grazie.
Patrick

16

Questo perché stai inquinando lo spazio dei nomi. Importerai tutte le funzioni e le classi nel tuo spazio dei nomi, che potrebbe scontrarsi con le funzioni che definisci tu stesso.

Inoltre, penso che l'uso di un nome qualificato sia più chiaro per l'attività di manutenzione; nella riga di codice viene visualizzato da dove proviene una funzione, in modo da poter consultare i documenti molto più facilmente.

Nel modulo foo:

def myFunc():
    print 1

Nel tuo codice:

from foo import *

def doThis():
    myFunc() # Which myFunc is called?

def myFunc():
    print 2


9

Supponi di avere il seguente codice in un modulo chiamato foo:

import ElementTree as etree

e poi nel tuo modulo hai:

from lxml import etree
from foo import *

Ora hai un modulo di cui è difficile eseguire il debug che sembra avere l'etichetta di lxml, ma ha invece ElementTree.


7

Queste sono tutte buone risposte. Aggiungo che quando si insegna a nuove persone a scrivere codice in Python, import *è molto difficile occuparsene . Anche se tu o loro non avete scritto il codice, è ancora un ostacolo.

Insegno ai bambini (circa 8 anni) a programmare in Python per manipolare Minecraft. Mi piace offrire loro un utile ambiente di codifica con cui lavorare ( Atom Editor ) e insegnare lo sviluppo guidato da REPL (tramite bpython ). In Atom trovo che i suggerimenti / completamenti funzionino altrettanto efficacemente di bpython. Fortunatamente, a differenza di altri strumenti di analisi statistica, Atom non viene ingannato import *.

Comunque, facciamo questo esempio ... In questo wrapper hanno from local_module import *un sacco di moduli incluso questo elenco di blocchi . Ignoriamo il rischio di collisioni dello spazio dei nomi. In questo from mcpi.block import *modo rendono l'intero elenco di tipi oscuri di blocchi qualcosa che devi andare a vedere per sapere cosa è disponibile. Se invece lo avessero usato from mcpi import block, allora potevi digitare walls = block.e quindi comparirà un elenco di completamento automatico. Schermata Atom.io


6

Compresi i punti validi che la gente mette qui. Tuttavia, ho un argomento secondo cui, a volte, "l'importazione da stelle" potrebbe non essere sempre una cattiva pratica:

  • Quando voglio strutturare il mio codice in modo tale che tutte le costanti vadano ad un modulo chiamato const.py :
    • Se lo faccio import const, quindi per ogni costante, devo riferirlo come const.SOMETHING, che probabilmente non è il modo più conveniente.
    • Se lo faccio from const import SOMETHING_A, SOMETHING_B ..., allora ovviamente è troppo prolisso e sconfigge lo scopo della strutturazione.
    • Quindi mi sento in questo caso, fare una from const import *potrebbe essere una scelta migliore.

4

È una pratica molto CATTIVA per due motivi:

  1. Leggibilità del codice
  2. Rischio di sovrascrivere variabili / funzioni ecc

Per il punto 1 : vediamo un esempio di questo:

from module1 import *
from module2 import *
from module3 import *

a = b + c - d

Qui, vedendo il codice, nessuno avrà idea di quale modulo b, ced in realtà appartiene.

D'altra parte, se lo fai come:

#                   v  v  will know that these are from module1
from module1 import b, c   # way 1
import module2             # way 2

a = b + c - module2.d
#            ^ will know it is from module2

È molto più pulito per te e anche la nuova persona che si unisce al tuo team avrà un'idea migliore.

Per il punto 2 : diciamo entrambi module1e module2hanno una variabile come b. Quando io faccio:

from module1 import *
from module2 import *

print b  # will print the value from module2

Qui il valore da module1viene perso. Sarà difficile eseguire il debug del motivo per cui il codice non funziona anche se bviene dichiarato in module1e ho scritto il codice in attesa del mio codice da utilizzaremodule1.b

Se hai le stesse variabili in moduli diversi e non vuoi importare l'intero modulo, puoi anche fare:

from module1 import b as mod1b
from module2 import b as mod2b

2

Come test, ho creato un modulo test.py con 2 funzioni A e B, che stampano rispettivamente "A 1" e "B 1". Dopo aver importato test.py con:

import test

. . . Posso eseguire le 2 funzioni come test.A () e test.B () e "test" si presenta come un modulo nello spazio dei nomi, quindi se modifico test.py posso ricaricarlo con:

import importlib
importlib.reload(test)

Ma se faccio quanto segue:

from test import *

non c'è alcun riferimento a "test" nello spazio dei nomi, quindi non c'è modo di ricaricarlo dopo una modifica (per quanto posso dire), che è un problema in una sessione interattiva. Considerando che uno dei seguenti:

import test
import test as tt

aggiungerà "test" o "tt" (rispettivamente) come nomi dei moduli nello spazio dei nomi, il che consentirà il nuovo caricamento.

Se lo faccio:

from test import *

i nomi "A" e "B" vengono visualizzati nello spazio dei nomi come funzioni . Se modifico test.py e ripeto il comando precedente, le versioni modificate delle funzioni non vengono ricaricate.

E il seguente comando genera un messaggio di errore.

importlib.reload(test)    # Error - name 'test' is not defined

Se qualcuno sa come ricaricare un modulo caricato con "from module import *", si prega di inviare. Altrimenti, questo sarebbe un altro motivo per evitare il modulo:

from module import *

2

Come suggerito nei documenti, non dovresti (quasi) mai usare import *nel codice di produzione.

Mentre l'importazione *da un modulo è negativa, l' importazione * da un pacchetto è ancora peggio. Per impostazione predefinita, from package import *importa tutti i nomi definiti dai pacchetti __init__.py, inclusi eventuali sottomoduli del pacchetto caricati in precedenzaimport istruzioni .

Tuttavia, se il __init__.pycodice di un pacchetto definisce un elenco denominato __all__, viene considerato l'elenco dei nomi dei sottomoduli che devono essere importati quando from package import *viene rilevato.

Considera questo esempio (supponendo che non sia __all__definito in sound/effects/__init__.py):

# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround

# in your module
from sound.effects import *

L'ultima istruzione importerà i moduli echoe surroundnello spazio dei nomi corrente (possibilmente sovrascrivendo le definizioni precedenti) perché sono definiti nel sound.effectspacchetto quando importviene eseguita l' istruzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.