¿Usar 'importar módulo' o 'desde módulo importar'?
Intenté encontrar una guía completa sobre si es mejor usar import module
o from module import
. Acabo de empezar con Python y estoy intentando empezar teniendo en cuenta las mejores prácticas.
Básicamente, esperaba que alguien pudiera compartir sus experiencias, qué preferencias tienen otros desarrolladores y cuál es la mejor manera de evitar problemas en el futuro.
La diferencia entre import module
y from module import foo
es principalmente subjetiva. Elija el que más le guste y sea constante en su uso. Aquí hay algunos puntos que le ayudarán a decidir.
import module
- Ventajas:
- Menos mantenimiento de tus
import
estados de cuenta. No es necesario agregar ninguna importación adicional para comenzar a usar otro elemento del módulo.
- Menos mantenimiento de tus
- Contras:
- Escribir
module.foo
su código puede ser tedioso y redundante (el tedio se puede minimizar usandoimport module as mo
luego escribirmo.foo
)
- Escribir
from module import foo
- Ventajas:
- Menos escritura para usar
foo
- Más control sobre a qué elementos de un módulo se puede acceder
- Menos escritura para usar
- Contras:
- Para utilizar un nuevo artículo del módulo tienes que actualizar tu
import
estado de cuenta - Pierdes contexto sobre
foo
. Por ejemplo, está menos claro quéceil()
significa en comparación conmath.ceil()
- Para utilizar un nuevo artículo del módulo tienes que actualizar tu
Cualquiera de los métodos es aceptable, pero no lo utilice from module import *
.
Para cualquier conjunto de código grande razonable, si lo desea, import *
probablemente lo cementará en el módulo y no podrá eliminarlo. Esto se debe a que es difícil determinar qué elementos utilizados en el código provienen del 'módulo', lo que facilita llegar al punto en el que crees que import
ya no los usas, pero es extremadamente difícil estar seguro.
Hay otro detalle aquí, no mencionado, relacionado con la escritura en un módulo. Es cierto que esto puede no ser muy común, pero lo he necesitado de vez en cuando.
Debido a la forma en que funcionan las referencias y la vinculación de nombres en Python, si desea actualizar algún símbolo en un módulo, digamos foo.bar, desde fuera de ese módulo, y hacer que otro código de importación "vea" ese cambio, debe importar foo a de cierta manera. Por ejemplo:
módulo fo:
bar = "apples"
módulo a:
import foo
foo.bar = "oranges" # update bar inside foo module object
módulo b:
import foo
print foo.bar # if executed after a's "foo.bar" assignment, will print "oranges"
Sin embargo, si importa nombres de símbolos en lugar de nombres de módulos, esto no funcionará.
Por ejemplo, si hago esto en el módulo a:
from foo import bar
bar = "oranges"
Ningún código externo a
se verá bar
como "naranjas" porque mi configuración bar
simplemente afectó el nombre "barra" dentro del módulo a
, no "alcanzó" el foo
objeto del módulo ni actualizó su archivo bar
.
Aunque mucha gente ya ha explicado sobre import
vs import from
, quiero intentar explicar un poco más sobre lo que sucede bajo el capó y dónde están todos los lugares en los que cambia.
import foo
:
Importa foo
y crea una referencia a ese módulo en el espacio de nombres actual. Luego, debe definir la ruta completa del módulo para acceder a un atributo o método particular desde dentro del módulo.
Por ejemplo foo.bar
, pero nobar
from foo import bar
:
Importa foo
y crea referencias a todos los miembros enumerados ( bar
). No establece la variable foo
.
Por ejemplo bar
, pero no baz
ofoo.baz
from foo import *
:
Importa foo
y crea referencias a todos los objetos públicos definidos por ese módulo en el espacio de nombres actual (todo lo que figura en __all__
si __all__
existe; de lo contrario, todo lo que no comienza con _
). No establece la variable foo
.
Por ejemplo bar
, y baz
pero no _qux
o foo._qux
.
Ahora veamos cuando lo hacemos import X.Y
:
>>> import sys
>>> import os.path
Consulte sys.modules
con nombre os
y os.path
:
>>> sys.modules['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
Comprobar globals()
y locals()
dictar espacios de nombres con os
y os.path
:
>>> globals()['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> locals()['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> globals()['os.path']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'os.path'
>>>
En el ejemplo anterior encontramos que solo os
se inserta en el espacio de nombres local y global. Entonces, deberíamos poder usar:
>>> os
<module 'os' from
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> os.path
<module 'posixpath' from
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>>
Pero no path
.
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
>>>
Una vez que elimines el os
espacio de nombres from locals(), no podrás acceder os
tan bien como os.path
aunque existan en sys.modules:
>>> del locals()['os']
>>> os
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>> os.path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>
Ahora hablemos de import from
:
from
:
>>> import sys
>>> from os import path
Consulte sys.modules
con os
y os.path
:
>>> sys.modules['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
Descubrimos que sys.modules
encontramos lo mismo que antes usandoimport name
Bien, veamos cómo se ve en los dictados locals()
del globals()
espacio de nombres:
>>> globals()['path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> locals()['path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> globals()['os']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'os'
>>>
Puede acceder utilizando el nombre, path
no mediante os.path
:
>>> path
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> os.path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>
Eliminemos 'ruta' de locals()
:
>>> del locals()['path']
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
>>>
Un último ejemplo usando un alias:
>>> from os import path as HELL_BOY
>>> locals()['HELL_BOY']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> globals()['HELL_BOY']
<module 'posixpath' from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>>
Y ningún camino definido:
>>> globals()['path']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'path'
>>>
Ambas formas son compatibles por una razón: hay ocasiones en las que una es más apropiada que la otra.
import module
: agradable cuando utilizas muchos bits del módulo. El inconveniente es que deberá calificar cada referencia con el nombre del módulo.from module import ...
: es bueno que los elementos importados se puedan utilizar directamente sin el prefijo de nombre del módulo. El inconveniente es que debes enumerar cada cosa que usas y que no está claro en el código de dónde viene algo.
Cuál usar depende de qué hace que el código sea claro y legible, y tiene más que ver con las preferencias personales. Me inclino por import module
lo general porque en el código está muy claro de dónde proviene un objeto o función. Lo uso from module import ...
cuando uso mucho algún objeto/función en el código.