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?

È stato utile?

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()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top