Pythons __import__ funktioniert nicht wie erwartet
-
03-07-2019 - |
Frage
Wenn __import__
mit einem gepunkteten Namen, so etwas wie: somepackage.somemodule
, kehrte das Modul nicht somemodule
ist, was zurückgegeben wird, scheint meistens leer zu sein! was hier los?
Lösung
Von dem Python-docs auf __import__
:
__import__( name[, globals[, locals[, fromlist[, level]]]])
...
Wenn der Name Variable ist von der Form package.module normalerweise das Top-Level-Paket (der Name bis der erste Punkt) zurückgegeben wird, nicht die Modul mit Namen genannt. Wenn jedoch ein nicht leere fromlist Argument gegeben wird, das Modul mit Namen genannt wird zurückgegeben. Dies ist für die Kompatibilität getan mit der Bytecode für die erzeugten verschiedene Arten von Import-Anweisung; bei der Verwendung von „import spam.ham.eggs“, die Top-Level-Paket Spam muss gestellt werden im Importnamespace, aber wenn Verwendung „von spam.ham Import Eier“, die spam.ham subpackage muss verwendet werden, finden die Eier variabel. Als ein Abhilfe für dieses Verhalten, Nutzung getattr () zu extrahieren, die gewünschte Komponenten. Zum Beispiel könnten Sie definiert die folgenden Helfer:
def my_import(name): mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod
paraphrasieren:
Wenn Sie sich für somepackage.somemodule
fragen, __import__
gibt somepackage.__init__.py
, die oft leer ist.
Es wird wieder somemodule
wenn Sie fromlist
(eine Liste der Variablennamen innerhalb somemodule
Sie wollen, die nicht tatsächlich zurückgegeben) liefern
Sie können auch, wie ich es tat, verwenden Sie die Funktion, die sie vorschlagen.
Hinweis: Ich habe diese Frage voll, es selbst zu beantworten beabsichtigt. Es war ein großer Fehler in meinem Code, und es falsch diagnostiziert zu haben, es dauerte eine lange Zeit, um herauszufinden, so dass ich dachte ich das SO Gemeinschaft helfen würde und poste Gotcha ich in hier lief.
Andere Tipps
Python 2.7 hat importlib, punktierte Pfade aufzulösen wie erwartet
import importlib
foo = importlib.import_module('a.dotted.path')
instance = foo.SomeClass()
Es gibt eine einfachere Lösung, wie in der Dokumentation erläutert:
Wenn Sie wollen einfach nur ein Modul importieren (möglicherweise in einem Paket) mit Namen, können Sie rufen __import __ () und es dann in sys.modules nachschlagen:
>>> import sys
>>> name = 'foo.bar.baz'
>>> __import__(name)
<module 'foo' from ...>
>>> baz = sys.modules[name]
>>> baz
<module 'foo.bar.baz' from ...>
Es ist etwas, das funktioniert, wie Sie es wollen: 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'>
Für Python 2.6, Ich schrieb dieses Snippet:
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
So wie ich das tat, ist
foo = __import__('foo', globals(), locals(), ["bar"], -1)
foobar = eval("foo.bar")
dann kann ich alle Inhalte zugreifen, indem
foobar.functionName()