__import__ в Python работает не так, как ожидалось

StackOverflow https://stackoverflow.com/questions/211100

  •  03-07-2019
  •  | 
  •  

Вопрос

При использовании __import__ с именем, обозначенным точкой, что-то вроде: somepackage.somemodule, возвращаемый модуль не является somemodule, все, что возвращается, кажется в основном пустым!что здесь происходит?

Это было полезно?

Решение

Из документации по python на __import__:

__import__( name[, globals[, locals[, fromlist[, level]]]])

...

Если переменная name имеет вид package.module, обычно возвращается пакет верхнего уровня (имя вплоть до первой точки), а не модуль, названный по имени.Однако, когда задается непустой аргумент fromlist, возвращается модуль, названный по имени.Это сделано для совместимости с байт-кодом, сгенерированным для различных типов инструкций import;при использовании "импортировать спам.ветчину.яйца" необходимо указать пакет "спам" верхнего уровня в пространстве имен импорта, но при использовании "из spam.ham импортировать яйца", для поиска переменной eggs необходимо использовать подпакет spam.ham.В качестве обходного пути для такого поведения используйте getattr() для извлечения нужных компонентов.Например, вы могли бы определить следующий вспомогательный элемент:

def my_import(name):
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

Перефразируя:

Когда вы просите о somepackage.somemodule, __import__ ВОЗВРАТ somepackage.__init__.py, который часто бывает пустым.

Он вернется somemodule если вы предоставите fromlist (список имен переменных внутри somemodule вы хотите, которые на самом деле не возвращаются)

Вы также можете, как и я, использовать функцию, которую они предлагают.

Примечание:Я задал этот вопрос с полным намерением ответить на него сам.В моем коде была большая ошибка, и из-за неправильного диагноза мне потребовалось много времени, чтобы разобраться в ней, поэтому я решил помочь сообществу SO и опубликовать здесь информацию, с которой столкнулся.

Другие советы

python 2.7 имеет importlib, пунктирные пути разрешаются, как и ожидалось

import importlib
foo = importlib.import_module('a.dotted.path')
instance = foo.SomeClass()

Существует более простое решение, как описано в документации:

Если вы просто хотите импортировать модуль (потенциально внутри пакета) по имени, вы можете вызвать __import__(), а затем посмотреть его в sys.modules:

>>> import sys
>>> name = 'foo.bar.baz'
>>> __import__(name)
<module 'foo' from ...>
>>> baz = sys.modules[name]
>>> baz
<module 'foo.bar.baz' from ...>

Есть что-то, что работает так, как вы хотите: 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'>

Для python 2.6 я написал этот фрагмент:

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

То, как я это сделал, это

foo = __import__('foo',  globals(), locals(), ["bar"], -1)
foobar = eval("foo.bar")

тогда я смогу получить доступ к любому контенту с помощью

foobar.functionName()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top