'import module' vs. 'from module import function'


143

Ho sempre usato questo metodo:

from sys import argv

e usare argvsolo con argv . Ma esiste una convenzione per l'utilizzo di questo:

import sys

e usando l'argv di sys.argv

Il secondo metodo rende il codice auto documentato e io (davvero) aderisco ad esso. Ma il motivo per cui preferisco il primo metodo è che è veloce perché stiamo importando solo la funzione necessaria piuttosto che importare l'intero modulo (che contiene più funzioni inutili che Python sprecherà tempo a importarle). Nota che ho solo bisogno di Argv e tutte le altre funzioni di Sys sono inutili per me.

Quindi le mie domande sono. Il primo metodo rende davvero veloce lo script? Quale metodo è preferito di più? Perché?



Risposte:


175

L'importazione del modulo non spreca nulla ; il modulo viene sempre completamente importato (nella sys.modulesmappatura), quindi sia che tu usi import syso from sys import argvnon faccia alcuna probabilità.

L'unica differenza tra le due affermazioni è il nome associato; import sysassocia il nome sysal modulo (quindi sys-> sys.modules['sys']), mentre from sys import argvassocia un nome diverso argv, indicando direttamente l'attributo contenuto all'interno del modulo (quindi argv-> sys.modules['sys'].argv). Il resto del sysmodulo è ancora lì, indipendentemente dal fatto che tu usi qualcos'altro dal modulo.

Non vi è inoltre alcuna differenza di prestazioni tra i due approcci. Sì, sys.argvdeve cercare due cose; deve cercare sysnel tuo spazio dei nomi globale (trova il modulo), quindi cercare l'attributo argv. E sì, usando from sys import argvpuoi saltare la ricerca dell'attributo, poiché hai già un riferimento diretto all'attributo. Ma la importdichiarazione deve ancora fare quel lavoro, cerca lo stesso attributo durante l'importazione e dovrai usarla argv una sola volta . Se dovessi usarlo argvmigliaia di volte in un ciclo, forse potrebbe fare la differenza, ma in questo caso specifico non lo è.

La scelta tra l'uno o l'altro, quindi, dovrebbe basarsi sullo stile di codifica .

In un modulo di grandi dimensioni , certamente userei import sys; la documentazione del codice è importante e l'utilizzo sys.argvda qualche parte in un modulo di grandi dimensioni rende molto più chiaro ciò a cui ti riferisci di quanto non faccia argvmai.

Se l'unico posto che usi argvè in un '__main__'blocco per chiamare una main()funzione, usa sicuramente from sys import argvse ti senti più felice a riguardo:

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

Userei ancora import syslì da solo. A parità di condizioni (e lo sono esattamente in termini di prestazioni e numero di personaggi usati per scriverlo), per me è solo più facile da vedere.

Se stai importando qualcos'altro , forse entra in gioco la performance. Ma solo se usi un nome specifico in un modulo più volte , ad esempio in un ciclo critico. Ma poi creare un nome locale (all'interno di una funzione) sarà ancora più veloce:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)

1
C'è anche la situazione in cui si dispone di un pacchetto con sottopackages o moduli che espone un attributo di uno di quei subpackages / moduli nel pacchetto di livello superiore. L'uso from...importconsente di fare package.attributepiuttosto che package.subpackage_or_module.attribute, il che può essere utile se si hanno raggruppamenti logici o concettuali all'interno del pacchetto ma si desidera rendere le cose un po 'più convenienti per gli utenti del pacchetto. ( numpyfa qualcosa del genere, credo.)
JAB

Nel django hai un sacco di punti in cui le cose come from django.core.management.base import BaseCommandsono migliori, e qualsiasi altra cosa (specialmente import django) porterebbe a codice illeggibile. Quindi, mentre mi piace questa risposta, penso che ci siano alcune librerie (e soprattutto alcuni framework) in cui la convenzione è di violare la semplice importazione. Come sempre, usa il tuo giudizio su ciò che è meglio in una determinata situazione. Ma errare dal lato esplicito (in altre parole, sono d'accordo per la maggior parte).
neuronet

1
@JAB: è comunque possibile utilizzare import ... asper trovare il pacchetto a un nome diverso: import package.subpackage_or_module as shortname. from parent import subfa essenzialmente la stessa cosa.
Martijn Pieters,

43

Ci sono due ragioni a favore dell'uso import modulepiuttosto che from module import function.

Il primo è lo spazio dei nomi. L'importazione di una funzione nello spazio dei nomi globale comporta il rischio di collisioni di nomi.

Il secondo non è così rilevante per i moduli standard, ma significativo per i tuoi moduli, specialmente durante lo sviluppo. È l'opzione per reload()un modulo. Considera questo:

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

D'altro canto

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

Per quanto riguarda la velocità ...

stiamo importando solo la funzione necessaria anziché importare l'intero modulo (che contiene più funzioni inutili che Python sprecherà tempo a importarle)

Sia che importi un modulo o importi una funzione da un modulo, Python analizzerà l'intero modulo. In entrambi i casi il modulo viene importato. "Importare una funzione" non è altro che associare la funzione a un nome. In effetti import moduleè meno lavoro per interprete che from module import func.


6
reload () era incorporato in Python 2; non è più il caso di Python 3.
André,

Pensavo ci fossero implicazioni anche con le dipendenze circolari dalle importazioni?
ADP

18

Uso from imports ogni volta che migliora la leggibilità. Ad esempio, preferisco (i punti e virgola servono solo per risparmiare spazio qui):

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

invece di:

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

Quest'ultimo è più difficile da leggere (e scrivere) per me perché contiene così tante informazioni ridondanti. Inoltre, è utile sapere in anticipo quali parti di un modulo sto usando.

Preferisco i normali importse sto usando molti nomi brevi da un modulo:

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

O se un nome è così generico che non ha senso al di fuori del suo spazio dei nomi:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

Questa è la mia risposta preferita "Esplicito è meglio che implicito" a volte è in conflitto con leggibilità, semplicità e SECCO. Soprattutto quando si utilizza un framework come Django.
neuronet

18

Secondo me l'uso regolare importmigliora la leggibilità. Quando rivedo il codice Python mi piace vedere da dove proviene la funzione o la classe data proprio dove viene usata. Mi evita di scorrere fino alla cima del modulo per ottenere quelle informazioni.

Per quanto riguarda i nomi lunghi dei moduli, utilizzo semplicemente la asparola chiave e fornisco loro alias brevi:

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

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

Come eccezione uso sempre la from module import somethingnotazione quando mi occupo del __future__modulo. Non puoi farlo in un altro modo quando vuoi che tutte le stringhe siano unicode di default in Python 2, ad es

from __future__ import unicode_literals
from __future__ import print_function

Amen! "import as" è una combinazione vincente :-)
paj28

4

Sebbene import sysed from sys import agrventrambi importino l'intero sysmodulo, quest'ultimo utilizza l'associazione dei nomi in modo che solo il argvmodulo sia accessibile al resto del codice.

Per alcune persone questo sarebbe lo stile preferito poiché rende accessibile solo la funzione che hai esplicitamente dichiarato.

Tuttavia introduce potenziali conflitti di nomi. E se avessi un altro modulo chiamato argv? Nota che puoi anche importare esplicitamente la funzione e rinominare con from sys import argv as sys_argv, una convenzione che soddisfa l'importazione esplicita ed è meno probabile che abbia dato collisioni nello spazio dei nomi.


2
Quindi come va if sys_argv:meglio di if sys.argv:? So cosa significa la seconda affermazione, non ho idea di cosa significhi la prima forma senza tornare indietro alla bizzarra importazione.
msw,

1

Di recente ho posto questa domanda a me stesso. Ho cronometrato i diversi metodi.

richiede la libreria

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

libreria 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

libreria 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

libreria di sistema

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

Mi sembra che ci sia una leggera differenza nelle prestazioni.


Stai aggiungendo una ricerca di attributi. Per confrontare import modulecon from module import namecorrettamente, aggiungere che il nome di ricerca per il import modulecaso. Ad esempio, aggiungere la linea sys.argval artest, ecc. Ci sarà ancora una differenza, perché il lavoro svolto è leggermente diverso, poiché viene generato un bytecode diverso e vengono eseguiti diversi codepath.
Martijn Pieters,

2
Nota che affronto direttamente quella differenza nella mia risposta; ci sarà una differenza tra usare import sysquindi usare sys.argvmigliaia di volte in un ciclo invece di from sys import argvusare solo argv. Ma tu no. Per le cose che fai solo una volta a livello globale del tuo modulo, dovresti davvero ottimizzare per la leggibilità, non differenze microscopiche nei tempi.
Martijn Pieters,

1
Ahhhh! E pensavo di essere su qualcosa! :) Ho solo scremato la tua risposta. Sembra che abbia saltato la pistola su quello. È bello essere umiliati.
tmthyjames,

-1

Guardare i frammenti di codice pubblicati, importare interi moduli e fare riferimento module.functionè praticamente lo standard, almeno per i moduli standard. L'unica eccezione sembra esseredatetime

from datetime import datetime, timedelta

così puoi dire datetime.now()piuttosto che datetime.datetime.now().

Se sei preoccupato per le prestazioni, puoi sempre dire (ad esempio)

argv = sys.argv

e quindi esegui il codice critico per le prestazioni poiché la ricerca del modulo è già stata eseguita. Tuttavia, sebbene funzionerà con funzioni / metodi, la maggior parte degli IDE verrà confusa e (ad esempio) non visualizzerà un collegamento / firma sorgente per la funzione quando viene assegnata a una variabile.


-2

Voglio solo aggiungerlo se fai qualcosa del genere

from math import sin

(o qualsiasi altra libreria integrata come syso posix), sinverrà inclusa nella documentazione del modulo (ovvero quando lo fai >>> help(mymodule)o $ pydoc3 mymodule. Per evitarlo, importa utilizzando:

import math
from math import sin as _sin

PS: una libreria integrata è una libreria compilata dal codice C e inclusa in Python. argparse, osE ionon sono incorporati nei pacchetti

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.