'módulo de importación' vs. 'desde la función de importación del módulo'

143

Siempre he estado usando este método:

from sys import argv

y usar argvsolo con argv . Pero hay una convención de usar esto:

import sys

y usando el argv por sys.argv

El segundo método hace que el código se auto documenta y yo (realmente) me adhiero a él. Pero la razón por la que prefiero el primer método es que es rápido porque estamos importando solo la función que se necesita en lugar de importar todo el módulo (que contiene más funciones inútiles que Python perderá tiempo importándolas). Tenga en cuenta que solo necesito argv y todas las demás funciones de sys son inútiles para mí.

Entonces mis preguntas son. ¿El primer método realmente hace que el script sea rápido? ¿Qué método es el más preferido? ¿Por qué?

Santosh Kumar
fuente
2
Ver también stackoverflow.com/q/710551/321973
Tobias Kienzler

Respuestas:

175

Importar el módulo no desperdicia nada ; el módulo siempre se importa completamente (en el sys.modulesmapeo), por lo tanto, si lo usa import syso from sys import argvno tiene probabilidades.

La única diferencia entre las dos declaraciones es qué nombre está vinculado; import sysvincula el nombre sysal módulo (so sys-> sys.modules['sys']), mientras que from sys import argvvincula un nombre diferente argv, apuntando directamente al atributo contenido dentro del módulo (so argv-> sys.modules['sys'].argv). El resto del sysmódulo todavía está allí, ya sea que use algo más del módulo o no.

Tampoco hay diferencia de rendimiento entre los dos enfoques. Sí, sys.argvtiene que buscar dos cosas; tiene que buscar sysen su espacio de nombres global (encuentra el módulo), luego busque el atributo argv. Y sí, al usar from sys import argvpuede omitir la búsqueda de atributos, ya que ya tiene una referencia directa al atributo. Pero la importdeclaración aún tiene que hacer ese trabajo, busca el mismo atributo al importar, y solo necesitará usar argv una vez . Si tuviera que usar argvmiles de veces en un bucle, tal vez podría hacer una diferencia, pero en este caso específico realmente no lo hace.

La elección entre uno u otro debe basarse en el estilo de codificación .

En un módulo grande , ciertamente lo usaría import sys; la documentación del código es importante, y el uso sys.argven algún lugar de un módulo grande deja mucho más claro a qué se refiere que argvnunca.

Si el único lugar que usa argves en un '__main__'bloque para llamar a una main()función, utilícelo from sys import argvsi se siente más feliz al respecto:

if __name__ == '__main__':
    from sys import argv
    main(argv)

Todavía lo usaría import sysyo mismo. En igualdad de condiciones (y son, exactamente, en términos de rendimiento y número de caracteres utilizados para escribirlo), eso es más fácil para mí.

Si está importando algo completamente diferente , entonces quizás el rendimiento entre en juego. Pero solo si usa un nombre específico en un módulo muchas veces , en un ciclo crítico, por ejemplo. Pero luego crear un nombre local (dentro de una función) va a ser aún más rápido:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)
Martijn Pieters
fuente
1
También existe la situación en la que tiene un paquete con subpaquetes o módulos que expone un atributo de uno de esos subpaquetes / módulos en el paquete de nivel superior. El uso le from...importpermite hacer en package.attributelugar de hacerlo package.subpackage_or_module.attribute, lo cual puede ser útil si tiene agrupaciones lógicas o conceptuales dentro del paquete pero desea hacer las cosas un poco más convenientes para los usuarios de su paquete. ( numpyhace algo como esto, creo.)
JAB
En django tiene toneladas de lugares donde cosas como from django.core.management.base import BaseCommandson mejores, y cualquier otra cosa (especialmente import django) conduciría a un código ilegible. Entonces, aunque me gusta esta respuesta, creo que hay algunas bibliotecas (y especialmente algunos marcos) en las que la convención es violar la importación simple. Como siempre, use su criterio sobre lo que es mejor en una situación dada. Pero errar del lado de lo explícito (en otras palabras, estoy de acuerdo en su mayor parte).
neuronet
1
@JAB: todavía se puede utilizar import ... aspara encontrar el paquete a un nombre diferente: import package.subpackage_or_module as shortname. from parent import subhace, esencialmente, lo mismo.
Martijn Pieters
43

Hay dos razones a favor de usar en import modulelugar de from module import function.

Primero es el espacio de nombres. Importar una función en el espacio de nombres global conlleva riesgos de colisiones de nombres.

Segundo, no es tan relevante para los módulos estándar, sino significativo para sus propios módulos, especialmente durante el desarrollo. Es la opción de reload()un módulo. Considera esto:

from module import func
...
reload(module)
# func still points to the old code

Por otra parte

import module
...
reload(module)
# module.func points to the new code

En cuanto a la velocidad ...

estamos importando solo la función que se necesita en lugar de importar todo el módulo (que contiene más funciones inútiles que Python perderá tiempo importándolas)

Ya sea que importe un módulo o importe una función desde un módulo, Python analizará todo el módulo. De cualquier manera, el módulo se importa. "Importar una función" no es más que vincular la función a un nombre. De hecho import modulees menos trabajo para el intérprete que from module import func.

vartec
fuente
66
reload () fue incorporado en Python 2; ese ya no es el caso para Python 3.
André
¿Pensé que también había implicaciones que hacer con las dependencias de importación circulares?
ADP
18

Yo uso from imports siempre que mejora la legibilidad. Por ejemplo, prefiero (los puntos y comas son solo para ahorrar espacio aquí):

from collections import defaultdict
from foomodule import FooBar, FooBaz
from twisted.internet.protocol import Factory
defaultdict(); FooBar(); FooBaz(); Factory()

en lugar de:

import collections
import foomodule
import twisted.internet.protocol
collections.defaultdict(); foomodule.FooBar(); foomodule.FooBaz()
twisted.internet.protocol.Factory()

Lo último es más difícil de leer (y escribir) para mí porque contiene mucha información redundante. Además, es útil saber de antemano qué partes de un módulo estoy usando.

Prefiero imports regulares si estoy usando muchos nombres cortos de un módulo:

import sys
sys.argv; sys.stderr; sys.exit()

O si un nombre es tan genérico que no tiene sentido fuera de su espacio de nombres:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

fuente
Esta es mi respuesta favorita. 'Explícito es mejor que implícito' a veces entra en conflicto con la legibilidad, simplicidad y SECO. Especialmente cuando se usa un framework como Django.
neuronet
18

En mi opinión, el uso regular importmejora la legibilidad. Cuando reviso el código de Python, me gusta ver de dónde proviene la función o clase dada de donde se usa. Me ahorra desplazarme hacia la parte superior del módulo para obtener esa información.

En cuanto a los nombres largos del módulo, solo uso la aspalabra clave y les doy alias cortos:

import collections as col
import foomodule as foo
import twisted.internet.protocol as twip

my_dict = col.defaultdict()
foo.FooBar()
twip_fac = twip.Factory()

Como excepción, siempre uso la from module import somethingnotación cuando trato con el __future__módulo. Simplemente no puede hacerlo de otra manera cuando desea que todas las cadenas sean unicode por defecto en Python 2, por ejemplo

from __future__ import unicode_literals
from __future__ import print_function
golem
fuente
¡Amén! "importar como" es una combinación ganadora :-)
paj28
4

Si bien import sysy from sys import agrvtanto importar todo el sysmódulo, este último se usa el nombre de unión a lo que sólo el argvmódulo es accesible al resto del código.

Para algunas personas, este sería el estilo preferido ya que solo hace accesible la función que usted estableció explícitamente.

Sin embargo, introduce posibles conflictos de nombres. ¿Qué pasa si tienes otro módulo llamado argv? Tenga en cuenta que también puede importar explícitamente la función y cambiarle el nombre from sys import argv as sys_argv, una convención que cumple con la importación explícita y es menos probable que dé colisiones de espacio de nombres.

Duncan
fuente
2
Entonces, ¿cómo es if sys_argv:mejor que if sys.argv:? Sé lo que significa la segunda declaración, no tengo idea de lo que significa la primera forma sin dar marcha atrás a la extraña importación.
msw
1

Hace poco me hice esta pregunta. He cronometrado los diferentes métodos.

biblioteca de solicitudes

def r():
    import requests
    return 'hello'
timeit r() # output: 1000000 loops, best of 3: 1.55 µs per loop

def rg():
    from requests import get
    return 'hello'
timeit rg() # output: 100000 loops, best of 3: 2.53 µs per loop

biblioteca de beautifulsoup

def bs():
    import bs4
    return 'hello' 
timeit bs() # output: 1000000 loops, best of 3: 1.53 µs per loop

def be():
    from bs4 import BeautifulSoup
    return 'hello'
timeit be() # output: 100000 loops, best of 3: 2.59 µs per loop

biblioteca json

def js():
    import json
    return 'hello'
timeit js() # output: 1000000 loops, best of 3: 1.53 µs per loop

def jl():
    from json import loads
    return 'hello'
timeit jl() # output: 100000 loops, best of 3: 2.56 µs per loop

biblioteca sys

def s():
    import sys
    return 'hello'
timeit s() # output: 1000000 loops, best of 3: 1.55 µs per loop

def ar():
    from sys import argv
    return 'hello'
timeit ar() # output: 100000 loops, best of 3: 2.87 µs per loop

Me parece que hay una ligera diferencia en el rendimiento.

tmthyjames
fuente
Está agregando una búsqueda de atributos. Para comparar import modulecon la from module import nameforma correcta, añadir que la búsqueda de nombres para el import modulecaso. Por ejemplo, agregue la línea sys.argva la arprueba, etc. Todavía habrá una diferencia, porque el trabajo realizado es ligeramente diferente, ya que se genera un código de bytes diferente y se ejecutan diferentes rutas de código.
Martijn Pieters
2
Tenga en cuenta que abordo directamente esa diferencia en mi respuesta; habrá una diferencia entre usar y import sysluego usar sys.argvmiles de veces en un ciclo versus from sys import argvluego usar solo argv. Pero tu no. Para las cosas que hace solo una vez en el nivel global de su módulo, realmente debe optimizar la legibilidad, no las diferencias microscópicas en los tiempos.
Martijn Pieters
1
Ahhhh! ¡Y pensé que estaba haciendo algo! :) Solo leí tu respuesta. Parece que le saqué el arma a esa. Se siente bien ser humillado.
tmthyjames
-1

Mirar los fragmentos de código publicados, importar módulos completos y hacer referencia module.functiones prácticamente el estándar, al menos para los módulos estándar. La única excepción parece serdatetime

from datetime import datetime, timedelta

por lo que se puede decir datetime.now()en lugar de datetime.datetime.now().

Si le preocupa el rendimiento, siempre puede decir (por ejemplo)

argv = sys.argv

y luego haga su código crítico de rendimiento ya que la búsqueda del módulo ya está hecha. Sin embargo, aunque esto funcionará con funciones / métodos, la mayoría de los IDE se confundirán y no mostrarán (por ejemplo) un enlace / firma de origen para la función cuando se asigna a una variable.

Austin
fuente
-2

Solo quiero agregar eso si haces algo como

from math import sin

(o cualquier otra biblioteca incorporada como syso posix), sinse incluirá en la documentación de su módulo (es decir, cuando lo haga >>> help(mymodule)o $ pydoc3 mymodule. Para evitar esto, importe utilizando:

import math
from math import sin as _sin

PD: una biblioteca integrada es aquella que se compila a partir del código C y se incluye con Python. argparse, osy iono son paquetes integrados

Guiño épico
fuente