2010-06-22 21 views
104

¿Cuáles son los pros y los contras de la importación de un módulo y/o función de Python en el interior de una función, con respecto a la eficiencia de la velocidad y de la memoria?En Python, ¿qué sucede cuando importas dentro de una función?

¿Se vuelve a importar cada vez que se ejecuta la función, o tal vez solo una vez al principio si la función se ejecuta o no?

+3

No hay ningún beneficio de velocidad (llamar 'import' es muy caro, incluso si el módulo ya está cargado). Si desea un beneficio de velocidad, es más rápido (si accede al módulo al menos 4-5 veces) simplemente asignar el módulo a una variable local como lo primero que hace en su función, y luego acceder a través de esa variable local (porque las búsquedas de variables locales son MUY rápidas). –

+3

(Avance rápido hasta las 26:30 en el video en http://us.pycon.org/2010/conference/schedule/event/71/ si desea un ejemplo interesante de cuán ridículamente mala puede ser la importación en una función) –

+0

@Nick: al escuchar esto, parece que es lento importarlo repetidamente, ya que cada vez que lo intenta, comprueba si se importó o no. ¿Estás diciendo que, fuera de la función, la importa y la configura como una variable global y capta la variable global dentro de la función? –

Respuesta

89

¿Se vuelve a importar cada vez que se ejecuta la función?

No; o más bien, los módulos de Python se almacenan en caché esencialmente cada vez que se importan, por lo que importar un segundo (o un tercio, o un cuarto ...) tiempo no los obliga a volver a pasar por todo el proceso de importación.

¿Importa una vez al principio si la función se ejecuta o no?

No, sólo se importa, siempre y cuando se ejecuta la función.

En cuanto a los beneficios: depende, supongo. Si sólo se puede ejecutar una función muy rara vez y no es necesario el módulo importado en otro sitio, se puede ser beneficioso para importación exclusivamente en esa función. O si hay un choque nombre u otra razón por la que no desea que el módulo o símbolos desde el módulo disponible en todas partes , es posible que desee importar en una función específica. (Por supuesto, siempre hay from my_module import my_function as f para esos casos.)

En la práctica general, probablemente no sea tan beneficioso. De hecho, la mayoría de las guías de estilo de Python alientan a los programadores a colocar todas las importaciones al principio del archivo del módulo.

+6

En la misma línea de pensamiento, esto puede hacer que una dependencia sea opcional si la importación está metida dentro de una función auxiliar. –

+1

Lo uso para dependencias opcionales cuando escribo módulos de biblioteca para mí. Hago que cada función dentro de un módulo de biblioteca dependa de un número mínimo de importaciones. – CodeMonkey

9

Importa una vez cuando la función se ejecuta por primera vez.

Pros:

  • importaciones relacionadas con la función que están acostumbrados en
  • fácil de mover las funciones de todo el paquete de

Contras:

  • no podía ver lo que los módulos de este módulo podría depender de
+0

si haces algo como 'grep import/path/to/module' te mostrará todos los módulos que importa. – dashaxiong

2

Importa una vez cuando la función es llamada por primera vez.

Podría imaginar hacerlo de esta manera si tuviera una función en un módulo importado que se usa muy raramente y es el único que requiere la importación. Sin embargo, parece bastante descabellado ...

5

La importación dentro de una función importará efectivamente el módulo una vez ... la primera vez que se ejecuta la función.

Debería importar igual de rápido si se importa en la parte superior, o cuando se ejecute la función. Esto no es generalmente una buena razón para importar en una def. Pros? No se importará si no se llama a la función ... Esto es realmente una razón razonable si su módulo solo requiere que el usuario tenga un determinado módulo instalado si usa funciones específicas de usted ...

Si eso es no es que él razone que estás haciendo esto, es casi seguro que es una idea asquerosa.

32

La primera vez que import goo desde cualquier lugar (dentro o fuera de una función), goo.py (u otra forma importable) se carga y sys.modules['goo'] se establece en el objeto de módulo así construido. Cualquier importación futura dentro de la misma ejecución del programa (de nuevo, ya sea dentro o fuera de una función) simplemente busque sys.modules['goo'] y conéctelo a barename goo en el alcance apropiado. La búsqueda Dict y el enlace de nombre son operaciones muy rápidas.

Suponiendo que el primer import consigue totalmente amortizados en ejecución del programa de todas formas, tener el "ámbito correspondiente" módulo de nivel significa que cada uso de goo.this, goo.that, etc, es decir dos búsquedas dict - uno para goo y otro para el nombre del atributo. Tenerlo como "nivel de función" paga una configuración de variable local adicional por ejecución de la función (¡incluso más rápido que la parte de búsqueda del diccionario!) Pero guarda una búsqueda dict (intercambiándola por una búsqueda de variable local, increíblemente rápida) para cada goo.this (etc.) acceden, básicamente, a la mitad del tiempo que tardan esas búsquedas.

Estamos hablando de unos nanosegundos de una forma u otra, por lo que no es una optimización que valga la pena. La ventaja potencialmente sustancial de tener el import dentro de una función es cuando esa función puede no ser necesaria en absoluto en una ejecución determinada del programa, por ejemplo, esa función se ocupa de errores, anomalías y situaciones raras en general; si ese es el caso, cualquier ejecución que no necesite la funcionalidad ni siquiera realizará la importación (y eso es un ahorro de microsegundos, no solo nanosegundos), solo las ejecuciones que necesiten la funcionalidad pagarán el precio (modesto pero mensurable).

Todavía es una optimización que solo vale la pena en situaciones bastante extremas, y hay muchas otras que consideraría antes de tratar de exprimir microsegundos de esta manera.

+0

Esta optimización en realidad nunca vale la pena: ninguna cantidad de acceso variable local rápido compensará el gasto increíble de llamar a 'import', incluso cuando el módulo ya está cargado. Verificar si un módulo se ha cargado es una operación muy costosa (en relación con algunas búsquedas globales de diccionarios). –

+2

La importación de un módulo por primera vez es costosa. Intente ejecutar un script vacío frente a uno que contenga solo 'import string, itertools, fractions, heapq, re, array, bisect, collections, math, os'. La primera toma un promedio de 180 ms, y la segunda de 230 ms. Entonces no son microsegundos para empezar. Son decenas de milisegundos (¿tal vez ocurre un acceso al disco?). Esto es importante para pequeños scripts que se ejecutan muchas veces (como, por ejemplo, para servir solicitudes web). –

+0

@EvgeniSergeev En ese caso, generalmente tiene un servidor ejecutándose todo el tiempo, por lo que no se volverá a importar una y otra vez –

3

Podría sugerir en general que, en lugar de preguntar, "¿X mejorará mi rendimiento?" ¿usa el perfil para determinar dónde está su programa realmente gastando su tiempo y luego aplica optimizaciones según dónde obtendrá el mayor beneficio?

Y luego puede utilizar la creación de perfiles para asegurarse de que sus optimizaciones también lo hayan beneficiado.

+2

Estoy de acuerdo con eso, esta fue más una pregunta de curiosidad. Me preguntaba cómo funciona el método de importación de Python con más detalle, más que tratar de hacer una mejora prematura del rendimiento. Gracias, sin embargo :) –

+2

Ah. Bueno, espero que las excelentes respuestas aquí hayan satisfecho tu curiosidad. Effbot tiene información que podría serle útil: http://effbot.org/zone/import-confusion.htm Desplácese hasta "¿Qué hace Python para importar un módulo?" – gomad

+0

Gracias por la información, las respuestas han sido excelentes y han sido de gran ayuda. –

Cuestiones relacionadas