¿Por qué es malo "importar *"?


153

Se recomienda no usar import *en Python.

¿Alguien puede compartir la razón de eso, para evitar que lo haga la próxima vez?



2
depende si está escribiendo scripts o escribiendo código que necesita reutilizar. A veces vale la pena ignorar los estándares de código. "importar *" también puede estar bien si tiene una convención de nomenclatura que deja en claro de dónde provienen las cosas. por ejemplo, "de Cats import *; TabbyCat; MaineCoonCat; CalicoCat;"
gatoatigrado

3
import *no funciona para mí en primer lugar en Python 2 o 3.
joshreesjones

1
¿Responde esto a tu pregunta? ¿Qué importa exactamente "import *"?
AMC

Respuestas:


223
  • Debido a que pone muchas cosas en su espacio de nombres (podría ocultar algún otro objeto de la importación anterior y no lo sabrá).

  • Porque no sabe exactamente qué se importa y no puede encontrar fácilmente desde qué módulo se importó una determinada cosa (legibilidad).

  • Porque no puedes usar herramientas geniales como pyflakesdetectar estáticamente errores en tu código.


2
Sí, realmente odio mi trabajo cuando alguien usa * import, porque entonces no puedo ejecutar pyflakes y ser feliz, sino que tengo que reparar esas importaciones. Sin embargo, es bueno que con ese copo de nieve me ayude a :-)
gruszczy

77
Como ejemplo concreto, muchos usuarios de NumPy han sido mordidos por el numpy.anysombreado anycuando lo hacen from numpy import *o una herramienta "útil" lo hace por ellos.
user2357112 es compatible con Monica el

1
¿Debo evitar usar el modificador --pylab para IPython por las mismas razones?
timgeb

66
Para resaltar un riesgo que nunca había pensado antes de leer esto ("podría ocultar algún otro objeto de la importación anterior"): import *hace que el orden de las importdeclaraciones sea significativo ... incluso para los módulos de biblioteca estándar que normalmente no se preocupan por el orden de importación . Algo tan inocente como alfabetizar sus importdeclaraciones podría romper su guión cuando una víctima anterior de la guerra de importación se convierte en el único sobreviviente. (Incluso si su script funciona ahora y nunca cambia, podría fallar repentinamente algún tiempo después si el módulo importado introduce un nuevo nombre que reemplaza al que confiaba).
Kevin J. Chase

49

De acuerdo con el Zen de Python :

Explícito es mejor que implícito.

... no se puede discutir con eso, ¿verdad?


29
En realidad, se puede discutir con eso. También es totalmente inconsistente, dado que no declara explícitamente las variables en Python, simplemente aparecen una vez que las asigna.
Konrad Rudolph

77
@gruszczy: ¿declarar variables es redundante para qué ? Asignando? No, son dos conceptos separados y declarar algo transmite una información muy distinta e importante. De todos modos, lo explícito siempre está vinculado a la redundancia, son dos caras de la misma moneda.
Konrad Rudolph

3
@kriss bien, pero ese no era mi punto. Mi punto fue que no declarar explícitamente una variable conduce a errores. Usted dice que "la asignación sin [declaración] es imposible". Pero eso está mal, mi punto es que Python desafortunadamente hace exactamente eso posible.
Konrad Rudolph

3
@kriss Otra pieza de información dada al compilador por la declaración es el hecho de que realmente tiene la intención de declarar una nueva variable. Esa es una información crucial para el sistema de tipos. Usted dice que los IDE modernos resuelven el problema de la escritura incorrecta, pero eso es simplemente incorrecto, y de hecho, este es un problema sustancial en lenguajes no compilados estáticamente, por lo que Perl agregó use strict(JavaScript var). Por otro lado, por supuesto, Python no es sin tipo (de hecho, está fuertemente tipado). De todos modos, incluso si tuviera razón, esto todavía contradiría el Zen de Python, citado en esta respuesta.
Konrad Rudolph el

3
@kriss Se equivoca: reutilizar el mismo nombre de variable no es un problema; reutilizar la misma variable sí (es decir, el mismo nombre en el mismo ámbito). Las declaraciones explícitas evitarían exactamente este error (y otros, basados ​​en errores tipográficos simples, que, como he dicho, en realidad es un problema extremadamente común y que consume mucho tiempo, a pesar de que tienes razón en que el problema es mayor en Perl-like idiomas). Y la contradicción a la que aludo es el requisito de explicidad del Zen, que aquí se arroja por la ventana.
Konrad Rudolph el

40

No pasas **locals()a las funciones, ¿verdad?

Dado que Python carece de una declaración de "inclusión", y el selfparámetro es explícito, y las reglas de alcance son bastante simples, generalmente es muy fácil señalar con el dedo una variable y decir de dónde proviene ese objeto, sin leer otros módulos y sin ningún tipo de IDE (que están limitadas en la forma de introspección de todos modos, por el hecho de que el lenguaje es muy dinámico).

El import *rompe todo eso.

Además, tiene una posibilidad concreta de ocultar errores.

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

Ahora, si el módulo de barra tiene alguno de los atributos " os", " mystuff", etc., anulará los importados explícitamente y posiblemente apuntará a cosas muy diferentes. La definición __all__en la barra a menudo es sabia, esto indica lo que se importará implícitamente, pero aún así es difícil rastrear de dónde provienen los objetos, sin leer y analizar el módulo de barra y seguir sus importaciones. Una red de import *es lo primero que soluciono cuando tomo posesión de un proyecto.

No me malinterpreten: si import *faltaran, lloraría por tenerlo. Pero tiene que ser usado con cuidado. Un buen caso de uso es proporcionar una interfaz de fachada sobre otro módulo. Del mismo modo, el uso de declaraciones de importación condicionales, o importaciones dentro de espacios de nombres de función / clase, requiere un poco de disciplina.

Creo que en proyectos medianos a grandes, o pequeños con varios contribuyentes, se necesita un mínimo de higiene en términos de análisis estático, ejecutando al menos pyflakes o incluso mejor un pylint configurado correctamente, para detectar varios tipos de errores antes Ellos pasan.

Por supuesto, dado que esto es python, siéntase libre de romper las reglas y explorar, pero tenga cuidado con los proyectos que podrían multiplicarse por diez, si el código fuente no tiene disciplina, será un problema.


66
Python 2.x hace tener una declaración de "incluir". Se llama execfile(). Afortunadamente, rara vez se usa y desaparece en 3.x.
Sven Marnach

¿Qué tal si **vars()incluimos globales si la función llamada está en otro archivo? : P
Solomon Ucko

16

Está bien hacerlo from ... import *en una sesión interactiva.


¿Qué tal dentro de una doctestcuerda? ¿Se import *interpreta dentro de una "caja de arena" en este caso? Gracias.
PatrickT

16

Eso es porque estás contaminando el espacio de nombres. Importará todas las funciones y clases en su propio espacio de nombres, lo que puede chocar con las funciones que usted mismo defina.

Además, creo que usar un nombre calificado es más claro para la tarea de mantenimiento; puede ver en la línea de código de dónde proviene una función, por lo que puede consultar los documentos con mucha más facilidad.

En el módulo foo:

def myFunc():
    print 1

En su código:

from foo import *

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

def myFunc():
    print 2


9

Digamos que tiene el siguiente código en un módulo llamado foo:

import ElementTree as etree

y luego en tu propio módulo tienes:

from lxml import etree
from foo import *

Ahora tiene un módulo difícil de depurar que parece que tiene etree de lxml, pero realmente tiene ElementTree en su lugar.


7

Estas son todas buenas respuestas. Voy a agregar que cuando enseño a nuevas personas a codificar en Python, lidiar con esto import *es muy difícil. Incluso si usted o ellos no escribieron el código, sigue siendo un obstáculo.

Enseño a niños (alrededor de 8 años) a programar en Python para manipular Minecraft. Me gusta darles un entorno de codificación útil para trabajar ( Atom Editor ) y enseñarles el desarrollo impulsado por REPL (a través de bpython ). En Atom, encuentro que las sugerencias / finalización funcionan tan efectivamente como bpython. Afortunadamente, a diferencia de otras herramientas de análisis estadístico, Atom no se deja engañar import *.

Sin embargo, tomemos este ejemplo ... En este contenedor , hay from local_module import *un montón de módulos que incluyen esta lista de bloques . Ignoremos el riesgo de colisiones en el espacio de nombres. Al hacerlo from mcpi.block import *, hacen que esta lista completa de tipos oscuros de bloques sea algo que debe mirar para saber qué hay disponible. Si en su lugar lo hubieran usado from mcpi import block, podría escribir walls = block.y luego aparecería una lista de autocompletar. Captura de pantalla de Atom.io


6

Entendido los puntos válidos que la gente pone aquí. Sin embargo, tengo un argumento que, a veces, "importar estrellas" no siempre es una mala práctica:

  • Cuando quiero estructurar mi código de tal manera que todas las constantes van a un módulo llamado const.py:
    • Si lo hago import const, entonces, para cada constante, tengo que referirme a esto const.SOMETHING, lo que probablemente no sea la forma más conveniente.
    • Si lo hago from const import SOMETHING_A, SOMETHING_B ..., entonces obviamente es demasiado detallado y frustra el propósito de la estructuración.
    • Por lo tanto, creo que en este caso, hacer una from const import *puede ser una mejor opción.

4

Es una práctica muy MALA por dos razones:

  1. Legibilidad de código
  2. Riesgo de anular las variables / funciones, etc.

Para el punto 1 : Veamos un ejemplo de esto:

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

a = b + c - d

Aquí, al ver el código, nadie tendrá idea de qué módulo b, cy en drealidad pertenece.

Por otro lado, si lo haces así:

#                   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

Es mucho más limpio para usted, y también la nueva persona que se una a su equipo tendrá una mejor idea.

Para el punto 2 : Digamos ambos module1y module2tengamos una variable como b. Cuando lo hago:

from module1 import *
from module2 import *

print b  # will print the value from module2

Aquí module1se pierde el valor de . Será difícil depurar por qué el código no funciona, incluso si bestá declarado module1y he escrito el código esperando que mi código usemodule1.b

Si tiene las mismas variables en diferentes módulos y no desea importar el módulo completo, incluso puede hacer lo siguiente:

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

2

Como prueba, creé un módulo test.py con 2 funciones A y B, que imprimen respectivamente "A 1" y "B 1". Después de importar test.py con:

import test

. . . Puedo ejecutar las 2 funciones como test.A () y test.B (), y "test" aparece como un módulo en el espacio de nombres, así que si edito test.py, puedo volver a cargarlo con:

import importlib
importlib.reload(test)

Pero si hago lo siguiente:

from test import *

no hay referencia a "prueba" en el espacio de nombres, por lo que no hay forma de volver a cargarlo después de una edición (por lo que puedo decir), lo cual es un problema en una sesión interactiva. Considerando que cualquiera de los siguientes:

import test
import test as tt

agregará "prueba" o "tt" (respectivamente) como nombres de módulo en el espacio de nombres, lo que permitirá volver a cargar.

Si lo hago:

from test import *

los nombres "A" y "B" aparecen en el espacio de nombres como funciones . Si edito test.py y repito el comando anterior, las versiones modificadas de las funciones no se vuelven a cargar.

Y el siguiente comando genera un mensaje de error.

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

Si alguien sabe cómo volver a cargar un módulo cargado con "from module import *", publique. De lo contrario, esta sería otra razón para evitar el formulario:

from module import *

2

Como se sugiere en los documentos, (casi) nunca debe usar import *en el código de producción.

Si bien la importación *desde un módulo es mala, la importación * desde un paquete es aún peor. De manera predeterminada, from package import *importa los nombres definidos por el paquete __init__.py, incluidos los submódulos del paquete que se cargaron en importdeclaraciones anteriores .

Sin embargo, si el __init__.pycódigo de un paquete define una lista llamada __all__, se considera que es la lista de nombres de submódulos que deben importarse cuando from package import *se encuentra.

Considere este ejemplo (suponiendo que no haya __all__definido en 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 *

La última instrucción importará los módulos echoy surrounden el espacio de nombres actual (posiblemente anulando las definiciones anteriores) porque se definen en el sound.effectspaquete cuando importse ejecuta la instrucción.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.