__Import__ de Python ne fonctionne pas comme prévu
-
03-07-2019 - |
Question
Lors de l'utilisation de __ import __
avec un nom en pointillé, quelque chose comme: somepackage.somemodule
, le module renvoyé n'est pas un module
, peu importe ce qui est renvoyé. semble être la plupart du temps vide! que se passe-t-il ici?
La solution
À partir de la documentation Python sur __ import __
:
__import__( name[, globals[, locals[, fromlist[, level]]]])
...
Quand la variable de nom est de la forme package.module, normalement, le paquet de niveau supérieur (le nom jusqu’à le premier point) est renvoyé, pas le module nommé par nom. Cependant, quand un l'argument fromlist non vide est donné, le module nommé par nom est renvoyé. Ceci est fait pour la compatibilité avec le bytecode généré pour le différents types de déclaration d'importation; lorsque vous utilisez "import spam.ham.eggs", le le spam de package de niveau supérieur doit être placé dans l'espace de noms d'importation, mais quand en utilisant "à partir de spam.ham importer des oeufs", le Le sous-paquet spam.ham doit être utilisé pour trouver la variable oeufs. Comme un solution de contournement pour ce comportement, utilisez getattr () pour extraire le Composants. Par exemple, vous pourriez définir l'assistant suivant:
def my_import(name): mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod
Pour paraphraser:
Lorsque vous demandez somepackage.somemodule
, __ import __
renvoie somepackage .__ init __. py
, qui est souvent vide.
Il retournera somemodule
si vous fournissez fromlist
(liste des noms de variables à l'intérieur de somemodule
que vous souhaitez, qui ne sont pas réellement renvoyés).
Vous pouvez également, comme je l'ai fait, utiliser la fonction proposée.
Remarque: j’ai posé cette question dans l’intention de répondre moi-même. Il y avait un gros problème dans mon code, et après l'avoir mal diagnostiqué, il m'a fallu beaucoup de temps pour le comprendre. J'ai donc décidé d'aider la communauté SO et de poster le piège que j'ai rencontré ici.
Autres conseils
python 2.7 contient importlib, les chemins en pointillés sont résolus comme prévu
import importlib
foo = importlib.import_module('a.dotted.path')
instance = foo.SomeClass()
Il existe une solution plus simple, comme expliqué dans la documentation:
Si vous souhaitez simplement importer un module (éventuellement dans un package) par son nom, vous pouvez appeler __import __ () puis le rechercher dans sys.modules:
>>> import sys
>>> name = 'foo.bar.baz'
>>> __import__(name)
<module 'foo' from ...>
>>> baz = sys.modules[name]
>>> baz
<module 'foo.bar.baz' from ...>
Quelque chose fonctionne comme vous le souhaitez: twisted.python.reflect.namedAny
:
>>> from twisted.python.reflect import namedAny
>>> namedAny("operator.eq")
<built-in function eq>
>>> namedAny("pysqlite2.dbapi2.connect")
<built-in function connect>
>>> namedAny("os")
<module 'os' from '/usr/lib/python2.5/os.pyc'>
Pour Python 2.6, j'ai écrit cet extrait:
def import_and_get_mod(str, parent_mod=None):
"""Attempts to import the supplied string as a module.
Returns the module that was imported."""
mods = str.split('.')
child_mod_str = '.'.join(mods[1:])
if parent_mod is None:
if len(mods) > 1:
#First time this function is called; import the module
#__import__() will only return the top level module
return import_and_get_mod(child_mod_str, __import__(str))
else:
return __import__(str)
else:
mod = getattr(parent_mod, mods[0])
if len(mods) > 1:
#We're not yet at the intended module; drill down
return import_and_get_mod(child_mod_str, mod)
else:
return mod
Ce que j'ai fait est
foo = __import__('foo', globals(), locals(), ["bar"], -1)
foobar = eval("foo.bar")
alors je peux accéder à tout contenu de par
foobar.functionName()