__Import__ di Python non funziona come previsto
-
03-07-2019 - |
Domanda
Quando si utilizza __import__
con un nome punteggiato, simile a: somepackage.somemodule
, il modulo restituito non è somemodule
, qualunque cosa venga restituita sembra essere per lo più vuoto! cosa sta succedendo qui?
Soluzione
Dai documenti di Python su __import__
:
__import__( name[, globals[, locals[, fromlist[, level]]]])
...
Quando la variabile del nome è del modulo package.module, normalmente, il pacchetto di livello superiore (il nome fino a viene restituito il primo punto), non il modulo chiamato per nome. Tuttavia, quando a viene fornito un argomento fromlist non vuoto, il modulo chiamato per nome viene restituito. Questo viene fatto per compatibilità con il bytecode generato per il diversi tipi di dichiarazione di importazione; quando si utilizza " import spam.ham.eggs " ;, il pacchetto spam di livello superiore deve essere inserito nello spazio dei nomi di importazione, ma quando utilizzando " da spam.ham importare uova " ;, il spam.ham subpackage deve essere utilizzato per trova le uova variabili. Come un soluzione alternativa per questo comportamento, utilizzare getattr () per estrarre il desiderato componenti. Ad esempio, potresti definire il seguente aiuto:
def my_import(name): mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod
Per parafrasare:
Quando chiedi somepackage.somemodule
, __import__
restituisce somepackage.__init__.py
, che è spesso vuoto.
Restituirà somemodule
se fornisci fromlist
(un elenco dei nomi delle variabili all'interno di somemodule
che desideri, che in realtà non vengono restituiti)
Puoi anche, come ho fatto io, usare la funzione che suggeriscono.
Nota: ho posto questa domanda con l'intenzione di rispondere da solo. C'era un grosso bug nel mio codice e, dopo averlo diagnosticato erroneamente, mi ci è voluto molto tempo per capirlo, quindi ho pensato di aiutare la comunità SO e pubblicare il gotcha in cui mi sono imbattuto qui.
Altri suggerimenti
python 2.7 ha importlib, i percorsi punteggiati si risolvono come previsto
import importlib
foo = importlib.import_module('a.dotted.path')
instance = foo.SomeClass()
Esiste una soluzione più semplice, come spiegato nella documentazione:
Se vuoi semplicemente importare un modulo (potenzialmente all'interno di un pacchetto) per nome, puoi chiamare __import __ () e cercarlo in sys.modules:
>>> import sys
>>> name = 'foo.bar.baz'
>>> __import__(name)
<module 'foo' from ...>
>>> baz = sys.modules[name]
>>> baz
<module 'foo.bar.baz' from ...>
C'è qualcosa che funziona come vuoi tu: 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'>
Per python 2.6, ho scritto questo 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
Il modo in cui l'ho fatto è
foo = __import__('foo', globals(), locals(), ["bar"], -1)
foobar = eval("foo.bar")
quindi posso accedere a qualsiasi contenuto da
foobar.functionName()