Как правильно использовать функцию импорта Python __import __ () - PullRequest
36 голосов
/ 21 марта 2012

Я пытаюсь повторить from foo.bar import object, используя функцию __import__, и мне кажется, что я ударил стену.

from glob import glob легко: glob = __import__("glob",glob) или glob = __import__("glob").glob

Проблема, с которой я столкнулся, заключается в том, что я импортирую из пакета (т. Е. Из бара), и я хочу, чтобы скрипт в пакете был источником импорта.

Так что мне бы хотелось что-токак

string_to_import = "bar"
object = __import__("foo",string_to_import).object

Но это просто импортирует __init__ в пакете foo.

Как это можно сделать?

РЕДАКТИРОВАТЬ: Когда я использую очевидное, только__init__ называется

__import__("foo.bar")
<module 'foo' from 'foo/__init__.pyc'>

Ответы [ 4 ]

37 голосов
/ 21 марта 2012

Функция __import__ вернет модуль верхнего уровня пакета, если вы не передадите непустой аргумент fromlist:

_temp = __import__('foo.bar', fromlist=['object']) 
object = _temp.object

См. Документы Python по функции __import__ .

31 голосов
/ 18 мая 2016

Как правильно использовать функцию Python __import__()?

Существует два вида использования:

  • прямой импорт
  • крючок для изменения поведения при импорте

В большинстве случаев вам и не нужно этого делать.

Для импорта из пользовательского пространства

Лучшая практика - использовать importlib. Но если вы настаиваете:

Тривиальное использование:

>>> sys = __import__('sys')
>>> sys
<module 'sys' (built-in)>

Complicated:

>>> os = __import__('os.path')
>>> os
<module 'os' from '/home/myuser/anaconda3/lib/python3.6/os.py'>
>>> os.path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>

Если вам нужен самый правый дочерний модуль в имени, передайте непустой список, например, [None], до fromlist:

>>> path = __import__('os.path', fromlist=[None])
>>> path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>

Или, как заявляет документация, используйте importlib.import_module:

>>> importlib = __import__('importlib')
>>> futures = importlib.import_module('concurrent.futures')
>>> futures
<module 'concurrent.futures' from '/home/myuser/anaconda3/lib/python3.6/concurrent/futures/__init__.py'>

Документация

Документы для __import__ являются наиболее запутанными из встроенных функций.

__import__(...)
    __import__(name, globals=None, locals=None, fromlist=(), level=0) -> module

    Import a module. Because this function is meant for use by the Python
    interpreter and not for general use it is better to use
    importlib.import_module() to programmatically import a module.

    The globals argument is only used to determine the context;
    they are not modified.  The locals argument is unused.  The fromlist
    should be a list of names to emulate ``from name import ...'', or an
    empty list to emulate ``import name''.
    When importing a module from a package, note that __import__('A.B', ...)
    returns package A when fromlist is empty, but its submodule B when
    fromlist is not empty.  Level is used to determine whether to perform 
    absolute or relative imports. 0 is absolute while a positive number
    is the number of parent directories to search relative to the current module.

Если вы внимательно его прочитаете, вы почувствуете, что API изначально предназначался для обеспечения отложенной загрузки функций из модулей. Тем не менее, это не то, как работает CPython, и я не знаю, сумели ли это сделать другие реализации Python.

Вместо этого CPython выполняет весь код в пространстве имен модуля при первом импорте, после чего модуль кэшируется в sys.modules.

__import__ все еще может быть полезным. Но понять, что он делает на основании документации, довольно сложно.

Полное использование __import__

Чтобы адаптировать полную функциональность для демонстрации текущего __import__ API, вот функция-оболочка с более чистым, лучше документированным API.

def importer(name, root_package=False, relative_globals=None, level=0):
    """ We only import modules, functions can be looked up on the module.
    Usage: 

    from foo.bar import baz
    >>> baz = importer('foo.bar.baz')

    import foo.bar.baz
    >>> foo = importer('foo.bar.baz', root_package=True)
    >>> foo.bar.baz

    from .. import baz (level = number of dots)
    >>> baz = importer('baz', relative_globals=globals(), level=2)
    """
    return __import__(name, locals=None, # locals has no use
                      globals=relative_globals, 
                      fromlist=[] if root_package else [None],
                      level=level)

Для демонстрации, например из сестринского пакета на базу:

baz = importer('foo.bar.baz')    
foo = importer('foo.bar.baz', root_package=True)
baz2 = importer('bar.baz', relative_globals=globals(), level=2)

assert foo.bar.baz is baz is baz2

Динамический доступ к именам в модуле

Для динамического доступа к глобальным именам по имени из модуля baz используйте getattr. Например:

for name in dir(baz):
    print(getattr(baz, name))

Крюк для изменения поведения при импорте

Вы можете использовать __import__, чтобы изменить или перехватить режим импорта. В этом случае давайте просто напечатаем аргументы, которые он получает, чтобы продемонстрировать, что мы его перехватываем:

old_import = __import__

def noisy_importer(name, locals, globals, fromlist, level):
    print(f'name: {name!r}')
    print(f'fromlist: {fromlist}')
    print(f'level: {level}')
    return old_import(name, locals, globals, fromlist, level)

import builtins
builtins.__import__ = noisy_importer

И теперь, когда вы импортируете, вы можете видеть эти важные аргументы.

>>> from os.path import join as opj
name: 'os.path'
fromlist: ('join',)
level: 0
>>> opj
<function join at 0x7fd08d882618>

Возможно, в этом контексте получение глобальных или локальных данных могло бы быть полезным, но об этом сразу не вспоминается.

20 голосов
/ 21 октября 2014

Вы должны использовать importlib.import_module, __import__ не рекомендуется вне интерпретатора.

В строке документа __import__:

Импортировать модуль.Поскольку эта функция предназначена для использования интерпретатором Python, а не для общего использования, лучше использовать importlib.import_module () для программного импорта модуля.

Она также поддерживает относительный импорт .

3 голосов
/ 21 марта 2012

Вместо использования функции __import__ я бы использовал функцию getattr:

model = getattr(module, model_s)

где module - это модуль для поиска, а model_s - строка вашей модели. Функция __import__ не предназначена для свободного использования, поскольку эта функция даст вам то, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...