74 votes

Pourquoi Python __import__ exiger fromlist?

En Python, si vous voulez importer par programmation d'un module, vous pouvez le faire:

module = __import__('module_name')

Si vous souhaitez importer un sous-module, vous pourriez penser que ce serait une simple question de:

module = __import__('module_name.submodule')

Bien sûr, cela ne fonctionne pas; vous obtenez juste module_name de nouveau. Que vous avez à faire:

module = __import__('module_name.submodule', fromlist=['blah'])

Pourquoi? La valeur réelle de l' fromlist ne semblent pas grave du tout, tant que c'est non-vide. Quel est le point de nécessiter un argument, puis en ignorant ses valeurs?

La plupart des choses en Python semble être fait pour une bonne raison, mais pour la vie de moi, je ne peux pas venir avec toute explication raisonnable à ce problème existent.

124voto

Thomas Wouters Points 38811

En fait, le comportement de l' __import__() est entièrement à cause de la mise en œuvre de l' import déclaration, qui demande __import__(). Il y a principalement cinq légèrement différentes façons __import__() peut être appelé en import (avec deux catégories principales):

import pkg
import pkg.mod
from pkg import mod, mod2
from pkg.mod import func, func2
from pkg.mod import submod

Dans le premier et le second cas, l' import déclaration doit attribuer le "plus à gauche" module objet de la "plus à gauche" nom: pkg. Après l' import pkg.mod que vous pouvez faire pkg.mod.func() parce que l' import déclaration introduit le nom local pkg, ce qui est un module objet qui a un mod d'attribut. Ainsi, l' __import__() de la fonction doit retourner le "plus à gauche" module objet de sorte qu'il peut être assigné à l' pkg. Ces deux déclarations d'importation se traduisent donc par:

pkg = __import__('pkg')
pkg = __import__('pkg.mod')

Dans les troisième, quatrième et cinquième cas, l' import déclaration a faire plus de travail: il a affecter (potentiellement) plusieurs noms, dont il a fait obtenir à partir de l'objet module. L' __import__() fonction ne peut retourner un objet, et il n'y a pas vraiment de raison de le faire récupérer chacun de ces noms de l'objet module (et ça ferait de la mise en œuvre beaucoup plus complexe.) Donc, l'approche la plus simple serait quelque chose comme (pour le troisième cas):

tmp = __import__('pkg')
mod = tmp.mod
mod2 = tmp.mod2

Toutefois, cela ne fonctionnera pas si pkg est un package et mod ou mod2 sont des modules dans le paquet qui ne sont pas déjà importés, comme ils le sont dans le troisième et le cinquième cas. L' __import__() fonction a besoin de savoir qu' mod et mod2 sont des noms que l' import déclaration voulez avoir accès, et donc qu'il peut voir si elles sont des modules et d'essayer de les importer. Donc, l'appel est plus proche de:

tmp = __import__('pkg', fromlist=['mod', 'mod2'])
mod = tmp.mod
mod2 = tmp.mod2

ce qui provoque __import__() de l'essayer et de la charge pkg.mod et pkg.mod2 ainsi que pkg (mais si mod ou mod2 n'existe pas, ce n'est pas une erreur dans l' __import__() appel; produire une erreur est laissé à l' import sur le relevé). Mais ce n'est toujours pas la bonne chose pour les quatrième et cinquième exemple, parce que si l'appel étaient donc:

tmp = __import__('pkg.mod', fromlist=['submod'])
submod = tmp.submod

ensuite, tmp est pkg, comme avant, et non pas l' pkg.mod module que vous souhaitez obtenir de l' submod attribut. La mise en œuvre pourrait avoir décidé de faire ainsi l' import déclaration de travaux supplémentaires, le fractionnement du nom de package sur . comme l' __import__() fonction déjà fait et traversant les noms, mais cela aurait signifié dupliquer les efforts. Donc, au lieu de cela, la mise en œuvre fait __import__() de retour le plus à droite du module au lieu de la plus à gauche d'un si et seulement si fromlist est passée et pas vide.

( import pkg as p Et from pkg import mod as m de la syntaxe ne change rien à cette histoire, à l'exception de ce qui les noms locaux affectés à -- l' __import__() fonction ne voit rien de différent lors de l' as est utilisé, tout cela reste dans l' import déclaration de mise en œuvre.)

5voto

zhkzyth Points 105

Je me sens toujours bizarre quand j'ai lu la réponse, donc j'ai essayé le code ci-dessous des échantillons.

Tout d'abord, essayez de construire en dessous de la structure du fichier:

tmpdir
  |A
     |__init__.py
     | B.py
     | C.py

Maintenant est un package, et B ou C est module. Ainsi, lorsque nous essayons un peu de code comme dans ipython:

Deuxièmement, exécutez l'exemple de code dans ipython:

  In [2]: kk = __import__('A',fromlist=['B'])

  In [3]: dir(kk)
  Out[3]: 
  ['B',
   '__builtins__',
   '__doc__',
   '__file__',
   '__name__',
   '__package__',
   '__path__']

Il semble que le fromlist fonctionne comme on s'y attendait. Mais les choses deviennent filaire lorsque nous essayons de faire la même chose sur un module. Supposons que nous avons un module appelé C. py et le code:

  handlers = {}

  def hello():
      print "hello"

  test_list = []

Alors maintenant, nous essayons de faire la même chose sur elle.

  In [1]: ls
  C.py

  In [2]: kk = __import__('C')

  In [3]: dir(kk)
  Out[3]: 
  ['__builtins__',
   '__doc__',
   '__file__',
   '__name__',
   '__package__',
   'handlers',
   'hello',
   'test_list']

Ainsi, lorsque nous voulons juste à importer l'test_list, ça fonctionne?

  In [1]: kk = __import__('C',fromlist=['test_list'])

  In [2]: dir(kk)
  Out[2]: 
  ['__builtins__',
   '__doc__',
   '__file__',
   '__name__',
   '__package__',
   'handlers',
   'hello',
   'test_list']

Le résultat montre que, lorsque nous essayons d'utiliser fromlist sur un module plutôt qu'un package, le fromlist param n'aide pas du tout car module a été compilé. Une fois qu'il est importé, il est impossible d'ignorer les autres.

2voto

mipadi Points 135410

La réponse peut être trouvée dans la documentation pour l' __import__:

Le fromlist doit être une liste de noms pour émuler from name import ..., ou d'une liste vide pour émuler import name.

Lors de l'importation d'un module à partir d'un package, notez qu' __import__('A.B', ...) renvoie Un paquet quand fromlist est vide, mais son sous-module B lorsque fromlist n'est pas vide.

Donc, fondamentalement, c'est juste la façon dont la mise en œuvre de l' __import__ fonctionne: si vous voulez que le sous-module, vous passez un fromlist contenant quelque chose que vous souhaitez importer à partir de la sous-module, et la mise en œuvre s' __import__ est telle que le sous-module est retourné.

De plus amples explications

Je pense que la sémantique existent afin que le module correspondant est retourné. En d'autres termes, dire que j'ai un paquet foo contenant le module bar avec la fonction baz. Si J':

import foo.bar

Puis je me réfère à l' baz comme

foo.bar.baz()

C'est comme __import__("foo.bar", fromlist=[]).

Si, au lieu-je importer avec:

from foo import bar

Puis je me réfère à l' bazcomme bar.baz()

Qui serait semblable à l' __imoort__("foo.bar", fromlist=["something"]).

Si je fais:

from foo.bar import baz

Puis je me réfère à l' baz comme

baz()

Qui est comme __import__("foo.bar", fromlist=["baz"]).

Ainsi, dans le premier cas, j'aurais du utiliser le nom pleinement qualifié, par conséquent __import__ renvoie le premier module nom que vous souhaitez utiliser pour désigner les éléments importés, foo. Dans le dernier cas, bar est la plus spécifique du module contenant les éléments importés, il est donc logique qu' __import__ serait de retour l' foo.bar module.

Le deuxième cas est un peu bizarre, mais je devine qu'il a été écrit de cette façon à la charge de l'importation d'un module à l'aide de l' from <package> import <module> de la syntaxe, et dans ce cas bar est toujours le plus spécifique du module de retour.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X