'module d'importation' vs 'depuis la fonction d'importation de module'

143

J'ai toujours utilisé cette méthode:

from sys import argv

et utiliser argvavec juste argv . Mais il existe une convention d'utilisation de ceci:

import sys

et en utilisant le argv par sys.argv

La deuxième méthode rend le code auto-documenté et j'y adhère (vraiment) . Mais la raison pour laquelle je préfère la première méthode est sa rapidité, car nous importons uniquement la fonction nécessaire plutôt que le module entier (qui contient davantage de fonctions inutiles que python perdra du temps à les importer). Notez que je n'ai besoin que d'argv et que toutes les autres fonctions de sys sont inutiles pour moi.

Donc mes questions sont. La première méthode rend-elle vraiment le script rapide? Quelle méthode est la plus préférée? Pourquoi?

Santosh Kumar
la source

Réponses:

175

Importer le module ne gaspille rien . le module est toujours entièrement importé (dans le sys.modulesmappage), que vous utilisiez import sysou from sys import argvnon.

La seule différence entre les deux déclarations concerne le nom lié. import syslie le nom sysau module (so sys-> sys.modules['sys']), tandis que from sys import argvlie un nom différent argv, en pointant directement sur l'attribut contenu à l'intérieur du module (so argv-> sys.modules['sys'].argv). Le reste du sysmodule est toujours là, que vous utilisiez autre chose du module ou non.

Il n'y a pas non plus de différence de performance entre les deux approches. Oui, sys.argvil faut regarder deux choses; il doit rechercher sysdans votre espace de noms global (trouve le module), puis rechercher l'attribut argv. Et oui, en utilisant, from sys import argvvous pouvez ignorer la recherche d'attribut, car vous avez déjà une référence directe à cet attribut. Mais l' importinstruction doit encore faire ce travail, elle recherche le même attribut lors de l'importation et vous n'aurez besoin que d' argv une seule utilisation . Si vous deviez utiliser des argvmilliers de fois dans une boucle, cela pourrait peut-être faire une différence, mais dans ce cas précis, ce n'est vraiment pas le cas.

Le choix entre l’un ou l’autre devrait alors reposer sur le style de codage .

Dans un grand module, j'utiliserais certainement import sys; la documentation de code est importante, et utiliser sys.argvquelque part dans un module volumineux rend beaucoup plus clair ce que vous faites référence à ce que vous argvauriez jamais fait.

Si le seul endroit que vous utilisez argvest dans un '__main__'bloc pour appeler une main()fonction, utilisez-le from sys import argvsi vous vous sentez plus heureux:

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

Je l'utilise encore import sysmoi-même. Toutes choses étant égales par ailleurs (et ce sont exactement les performances et le nombre de caractères utilisés pour l'écrire), c'est plus simple pour moi.

Si vous importez autre chose , alors peut-être que la performance entre en jeu. Mais seulement si vous utilisez plusieurs fois un nom spécifique dans un module , par exemple dans une boucle critique. Mais créer un nom local (au sein d'une fonction) sera encore plus rapide:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)
Martijn Pieters
la source
1
Il existe également le cas où vous avez un package avec des sous-packages ou des modules qui expose un attribut de l'un de ces sous-packages / modules dans le package de niveau supérieur. Utiliser from...importvous permet de faire package.attributeplutôt que de package.subpackage_or_module.attribute, ce qui peut être utile si vous avez des groupements logiques ou conceptuels dans le paquet, mais que vous voulez rendre les choses un peu plus pratiques pour les utilisateurs de votre paquet. ( numpyfait quelque chose comme ça, je crois.)
JAB
Dans Django, vous avez des tonnes d’espaces où les choses from django.core.management.base import BaseCommandsont meilleures, et toute autre chose (en particulier import django) conduirait à un code illisible. Donc, même si j'aime cette réponse, je pense qu'il y a des bibliothèques (et en particulier des cadres) dans lesquelles la convention doit violer la simple importation. Comme toujours, utilisez votre jugement pour déterminer ce qui convient le mieux dans une situation donnée. Mais pécher par excès (en d’autres termes, je suis d’accord pour la plupart).
Neuronet
1
@JAB: vous pouvez toujours utiliser import ... aspour trouver le package à un autre nom: import package.subpackage_or_module as shortname. from parent import subfait, essentiellement, la même chose.
Martijn Pieters
43

Il y a deux raisons pour utiliser import moduleplutôt que from module import function.

Le premier est l'espace de noms. L'importation d'une fonction dans l'espace de noms global entraîne des collisions de noms.

Deuxièmement, ce n'est pas pertinent pour les modules standard, mais important pour vos propres modules, en particulier pendant le développement. C'est l'option d' reload()un module. Considère ceci:

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

D'autre part

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

Quant à la vitesse ...

nous importons uniquement la fonction nécessaire plutôt que d'importer tout le module (qui contient davantage de fonctions inutiles que python perdra du temps à les importer)

Que vous importiez un module ou importiez une fonction à partir d'un module, Python analysera l'intégralité du module. De toute façon, le module est importé. "Importer une fonction" n'est rien de plus que lier la fonction à un nom. En fait, import modulec’est moins de travail pour interprète que from module import func.

vartec
la source
6
reload () était intégré à Python 2; ce n'est plus le cas pour Python 3.
André
Je pensais qu'il y avait aussi des implications à voir avec les dépendances d'importation circulaires?
ADP
18

J'utilise from imports chaque fois que cela améliore la lisibilité. Par exemple, je préfère (les points-virgules servent uniquement à économiser de l'espace ici):

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

au lieu de:

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

Ce dernier est plus difficile à lire (et à écrire) pour moi car il contient beaucoup d'informations redondantes. De plus, il est utile de savoir à l'avance quelles parties du module que j'utilise.

Je préfère les jeux réguliers importsi j'utilise beaucoup de noms abrégés d'un module:

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

Ou si un nom est tellement générique qu'il n'a pas de sens en dehors de son espace de noms:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

la source
Ceci est ma réponse préférée. 'Explicite vaut mieux qu'implicite', parfois en conflit avec la lisibilité, la simplicité et le DRY. Surtout lorsque vous utilisez un framework comme Django.
Neuronet
18

À mon avis, l'utilisation régulière importaméliore la lisibilité. Lors de l'examen du code Python, j'aime voir d'où vient la fonction ou la classe donnée, là où elle est utilisée. Cela me évite de faire défiler jusqu'en haut du module pour obtenir cette information.

En ce qui concerne les noms de modules longs, j'utilise simplement le asmot clé et leur donne des alias courts:

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

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

Exceptionnellement, j'utilise toujours la from module import somethingnotation lorsque je traite avec le __future__module. Vous ne pouvez tout simplement pas le faire autrement lorsque vous voulez que toutes les chaînes soient unicode par défaut dans Python 2, par exemple

from __future__ import unicode_literals
from __future__ import print_function
Golem
la source
Amen! "import as" est une combinaison gagnante :-)
paj28
4

Bien import sysque from sys import agrvtous les deux importent le sysmodule entier , ce dernier utilise la liaison de noms afin que seul le argvmodule soit accessible au reste du code.

Pour certaines personnes, ce serait le style préféré, car il ne rend accessible que la fonction que vous avez explicitement définie.

Il introduit toutefois des conflits de noms potentiels. Et si vous aviez un autre module nommé argv? Notez que vous pouvez également importer explicitement la fonction et renommer avec from sys import argv as sys_argv, une convention qui répond à l'importation explicite et est moins susceptible de provoquer des collisions d'espace de nom.

Duncan
la source
2
Alors, comment est-il if sys_argv:meilleur que if sys.argv:? Je sais ce que la deuxième déclaration signifie, je n'ai aucune idée de ce que la première forme signifie sans revenir en arrière à la bizarre importation.
msw
1

Je me suis récemment posé cette question. J'ai chronométré les différentes méthodes.

bibliothèque de demandes

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

bibliothèque 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

bibliothèque 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

bibliothèque système

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

Il me semble qu'il y a une légère différence de performance.

tmthyjames
la source
Vous ajoutez une recherche d'attribut. Pour comparer import moduleavec from module import namecorrectement, ajouter que la recherche de nom à l' import moduleaffaire. Par exemple, ajoutez la ligne sys.argvau artest, etc. Il y aura toujours une différence, car le travail effectué est légèrement différent, car un bytecode différent est généré et des chemins de code différents sont exécutés.
Martijn Pieters
2
Notez que je traite directement de cette différence dans ma réponse; il y aura une différence entre utiliser import syspuis utiliser des sys.argvmilliers de fois dans une boucle et from sys import argvensuite utiliser simplement argv. Mais tu ne le fais pas. Pour ce que vous faites une fois au niveau global de votre module, vous devez optimiser la lisibilité, et non les différences microscopiques de minutage.
Martijn Pieters
1
Ahhhh! Et je pensais que j'étais sur quelque chose! :) J'ai seulement écrémé votre réponse. On dirait que j'ai sauté le flingue sur celui-là. Ça fait du bien d'être humilié.
tmthyjames
-1

Examiner des fragments de code publiés, importer des modules entiers et s'y référer module.functionest à peu près la norme, du moins pour les modules standard. La seule exception semble êtredatetime

from datetime import datetime, timedelta

alors vous pouvez dire datetime.now()plutôt que datetime.datetime.now().

Si vous êtes préoccupé par la performance, vous pouvez toujours dire (par exemple)

argv = sys.argv

et puis faites votre code critique de performance puisque la recherche de module est déjà faite. Cependant, bien que cela fonctionne avec des fonctions / méthodes, la plupart des IDE seront confus et n’afficheront pas (par exemple) de lien source / signature pour la fonction quand elle est assignée à une variable.

Austin
la source
-2

Je veux juste ajouter que si vous faites quelque chose comme

from math import sin

(ou toute autre bibliothèque intégrée telle que sysou posix), sinsera alors inclus dans la documentation de votre module (c.-à-d. lorsque vous faites >>> help(mymodule)ou $ pydoc3 mymodule. Pour éviter cela, importez-le à l'aide de:

import math
from math import sin as _sin

PS: une bibliothèque intégrée est une bibliothèque compilée à partir de code C et incluse dans Python. argparse, oset ione sont pas des packages intégrés

Clin d'oeil épique
la source