2009-11-09 9 views
37

Creo que poner la instrucción de importación tan cerca del fragmento que la utiliza ayuda a la legibilidad al hacer que sus dependencias sean más claras. ¿Caché Python esto? ¿Debería importarme? ¿Es una mala idea?Declaraciones de importación local en Python

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

for i in xrange(10): Process() 

Un poco más de la justificación: es para los métodos que utilizan los bits arcanos de la biblioteca, pero cuando refactorizar el método en otro archivo, que no se dan cuenta que me perdí la dependencia externa hasta que consigo un error de ejecución.

+0

que sería muy bueno tener una "con (importación StringIO) como ModuleName: sintaxis – JeremyKun

+0

@Bean: Si eso es por lo que no tiene que escribir un nombre de módulo de largo o difícil de manejar, entonces no hay una manera fácil:' importa StringIO' y luego 'sio = StringIO'. Ahora puedes hacer' file_handle = sio.StringIO ('hello world') 'y guardar esos preciosos cinco caracteres. Sin embargo, usaría esto con moderación, porque puede hacer que el código sea más difícil para leer (la tarea es fácil de perder, los nombres de módulos no estándar pueden distraer) – cvoinescu

Respuesta

63

Las otras respuestas evidencian una leve confusión en cuanto a cómo import realmente funciona.

Esta afirmación:

import foo 

es más o menos equivalente a esta declaración:

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

Es decir, se crea una variable en el ámbito actual con el mismo nombre que el módulo solicitado, y cesionarios es el resultado de llamar al __import__() con ese nombre de módulo y una carga de argumentos predeterminados.

La función __import__() maneja conceptualmente convierte una cadena ('foo') en un objeto de módulo. Los módulos están en caché en sys.modules, y ese es el primer lugar que se ve __import__() - si sys.modules tiene una entrada para 'foo', eso es lo que __import__('foo') devolverá, cualquiera que sea. Realmente no me importa el tipo.Puedes ver esto en acción tú mismo; intente ejecutar el siguiente código:

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

Dejando a un lado las preocupaciones estilísticas, por el momento, con una sentencia de importación dentro de una función de cómo funciona te gustaría. Si el módulo nunca se importó anteriormente, se importa y almacena en caché en sys.modules. A continuación, asigna el módulo a la variable local con ese nombre. No no no modifica ningún estado de nivel de módulo. Es hace posiblemente modificar algún estado global (agregando una nueva entrada a sys.modules).

Dicho esto, casi nunca uso import dentro de una función. Si la importación del módulo crea una desaceleración notable en su programa -como hace una computación larga en su inicialización estática, o es simplemente un módulo masivo- y su programa rara vez necesita el módulo para nada, está perfectamente bien tener la importación solo dentro las funciones en que se usa. (Si esto era desagradable, Guido saltaría en su máquina del tiempo y cambiaría Python para evitar que lo hagamos). Pero como regla, yo y la comunidad general de Python ponemos todas nuestras declaraciones de importación en la parte superior del módulo en el alcance del módulo.

+15

también ocasionalmente lo salvan de las importaciones cíclicas (por ejemplo: si necesita importar un modelo en su archivo managers.py con django y los modelos.py importa el archivo managers.py ya ... que generalmente lo hace) – Jiaaro

+4

Además la respuesta es clara, estoy feliz de que haya usado 3 _not_, ya que 2 o 4 serían confusos. – matiasg

10

Por favor, vea PEP 8:

importaciones siempre se colocan en la parte superior de el archivo, justo después de cualquier módulo comentarios y cadenas de documentación, y antes de variables globales del módulo y constantes.

Tenga en cuenta que esto es puramente una elección estilística como Python tratará a todos los import declaraciones el mismo, independientemente de donde se declaran en el archivo de origen. Sin embargo, le recomendaría que siga la práctica común, ya que hará que su código sea más legible para los demás.

+2

enlace interesante, pero: "Dos buenas razones para romper una regla en particular: (1) Al aplicar la regla, el código sería menos legible, incluso para alguien que está acostumbrado a leer código que sigue las reglas. ... " –

+0

Esto solo se aplica a las importaciones de nivel superior. Una importación dentro de una función no se procesará hasta que se invoque esa función. Aunque generalmente desalentaría hacer esto, puede ser útil cuando tienes una dependencia que es o bien es muy costoso de cargar o puede no estar disponible en todos los entornos. –

9

Aparte del estilo, es cierto que un módulo importado solo se importará una vez (a menos que se llame a reload en dicho módulo). Sin embargo, cada llamada al import Foo comprobará implícitamente si ese módulo ya está cargado (marcando sys.modules).

considerar también la "desmontaje" de dos funciones lo demás iguales, donde uno trata de importar un módulo y el otro 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   

No estoy seguro de cuánto más el código de bytes se traduce para el máquina virtual, pero si esto era un ciclo interno importante para su programa, ciertamente querría poner un poco de peso en el enfoque Bar sobre el enfoque Foo.

Una prueba rápida y sucia timeit sí muestra una mejora modesta velocidad cuando se utiliza 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

Cuando el intérprete de Python realiza un sentencia de importación, se inicia la lectura de todas las definiciones de funciones en el archivo que se está importando . Esto explica por qué a veces las importaciones pueden demorar un tiempo.

La idea de realizar todas las importaciones desde el principio es una convención estilística como señala Andrew Hare. Sin embargo, debe tener en cuenta que al hacerlo, implícitamente hace que el intérprete compruebe si este archivo ya se importó después de la primera vez que lo importó. También se convierte en un problema cuando su archivo de código se vuelve grande y desea "actualizar" su código para eliminar o reemplazar ciertas dependencias. Esto requerirá que busque todo el archivo de código para encontrar todos los lugares donde ha importado este módulo.

Sugeriría seguir la convención y mantener las importaciones en la parte superior de su archivo de código. Si realmente desea realizar un seguimiento de las dependencias de las funciones, le sugiero que las agregue en el docstring para esa función.

+1

"Cuando el intérprete de Python golpea una declaración de importación, comienza a leer todas las definiciones de función en el archivo" - (1) ocurre solo la PRIMERA vez que el módulo se importa durante la ejecución actual del intérprete de Python (y el módulo importado está escondido en sys.modules de modo que solo se requiere una búsqueda de nombre la próxima vez (2) "leer definiciones de funciones" NO es lo que hace; EJECUTA todo el módulo código de nivel (en su mayoría declaraciones'def' y 'class'). Otras cosas, por ejemplo, más importaciones, la configuración de estructuras de datos a nivel de módulo (a veces de archivos) puede llevar un tiempo. (3) irrelevante. –

0

Veo dos maneras cuando se necesita para importar de forma local

  1. con fines de prueba o para uso temporal, es necesario importar algo, en ese caso se debería poner la importación en el lugar de uso.

  2. En algún momento para evitar la dependencia cíclica tendrá que importarlo dentro de una función pero eso significaría que tiene un problema en otro lugar.

De lo contrario, siempre lo pone en la parte superior para mayor eficiencia y consistencia.

8

he hecho esto, y luego deseé no haberlo hecho. Normalmente, si estoy escribiendo una función y esa función necesita usar StringIO, puedo mirar la parte superior del módulo, ver si se está importando y luego agregarlo si no lo está.

Supongamos que yo no hago esto; supongo que lo agrego localmente dentro de mi función. Y luego supongamos que en algún punto I, u otra persona, agrega un montón de otras funciones que usan StringIO. Esa persona mirará la parte superior del módulo y agregará import StringIO. Ahora su función contiene código que no solo es inesperado sino redundante.

Además, se viola lo que creo que es un principio muy importante: no modificar directamente el estado de nivel de módulo desde el interior de una función.

Editar:

En realidad, resulta que todo lo anterior no tiene sentido.

Importación de un módulo no modificar el estado de nivel de módulo (que inicializa el módulo que se importa, si nada más aún, pero eso no es en absoluto la misma cosa). Importar un módulo que ya ha importado en otro lugar no le cuesta nada, excepto una búsqueda en sys.modules y la creación de una variable en el ámbito local.

Sabiendo esto, me siento un poco tonto fijación de todos los lugares de mi código en la que me fijo, pero esa es mi cruz.

+1

lo hice en negrita uso no había pensado en eso y es realmente obvio en retrospectiva. –

+0

Si con esto quiere decir "importar StringIO" en el medio de una función, cambia cualquier cosa para el alcance del módulo donde se definió la función ... esto es incorrecto. Como explico en mi respuesta, ejecutar una "importación" en el medio de una función no cambia nada en el estado del módulo donde se definió esa función. ¿Y qué hay de malo en modificar el estado de nivel de módulo desde dentro de una función de todos modos? Hay una palabra clave completa dedicada a hacer exactamente eso: "global". –

+0

+1 por franqueza. –

Cuestiones relacionadas