Почему Python __import__ требует fromlist?
В Python, если вы хотите программно импортировать модуль, вы можете сделать:
module = __import__('module_name')
Если вы хотите импортировать подмодуль, вы бы подумали, что это будет простой вопрос:
module = __import__('module_name.submodule')
Конечно, это не работает; вы снова получите module_name
. Вы должны сделать:
module = __import__('module_name.submodule', fromlist=['blah'])
Почему? Фактическое значение fromlist
, похоже, не имеет значения, если оно не пустое. Какой смысл требовать аргумент, затем игнорируя его значения?
Большинство вещей в Python, кажется, сделано по уважительной причине, но для моей жизни я не могу придумать разумного объяснения этого поведения.
Ответы
Ответ 1
На самом деле поведение __import__()
происходит целиком из-за реализации инструкции import
, которая вызывает __import__()
. В основном пять различных способов __import__()
могут быть вызваны import
(с двумя основными категориями):
import pkg
import pkg.mod
from pkg import mod, mod2
from pkg.mod import func, func2
from pkg.mod import submod
В первом и втором случаях оператор import
должен назначить объекту модуля "самый левый" имя "left-most": pkg
. После import pkg.mod
вы можете сделать pkg.mod.func()
, потому что оператор import
ввел локальное имя pkg
, которое является объектом модуля с атрибутом mod
. Таким образом, функция __import__()
должна возвращать объект модуля "самый левый", поэтому его можно назначить на pkg
. Таким образом, эти два оператора импорта преобразуются в:
pkg = __import__('pkg')
pkg = __import__('pkg.mod')
В третьем, четвертом и пятом случае оператор import
должен выполнять больше работы: он должен назначить (потенциально) несколько имен, которые он должен получить от объекта модуля. Функция __import__()
может возвращать только один объект, и нет никакой реальной причины, чтобы заставить это получить каждое из этих имен из объекта модуля (и это сделало бы реализацию намного сложнее.) Таким образом, простой подход был бы чем-то вроде ( для третьего случая):
tmp = __import__('pkg')
mod = tmp.mod
mod2 = tmp.mod2
Однако это не сработает, если pkg
является пакетом, а mod
или mod2
являются модулями этого пакета, которые еще не импортированы, поскольку они находятся в третьем и пятом случае. Функция __import__()
должна знать, что mod
и mod2
являются именами, которые оператор import
должен иметь доступный, чтобы он мог видеть, являются ли они модулями и пытаются их импортировать. Таким образом, вызов ближе к:
tmp = __import__('pkg', fromlist=['mod', 'mod2'])
mod = tmp.mod
mod2 = tmp.mod2
который вызывает __import__()
, чтобы попытаться загрузить pkg.mod
и pkg.mod2
, а также pkg
(но если mod
или mod2
не существует, это не ошибка в вызове __import__()
, создавая ошибку, остается инструкция import
). Но это все еще не подходит для четвертого и пятого примеров, потому что если вызов был таким:
tmp = __import__('pkg.mod', fromlist=['submod'])
submod = tmp.submod
тогда tmp
окажется pkg
, как и раньше, а не модулем pkg.mod
, из которого вы хотите получить атрибут submod
. Реализация могла бы решить сделать так, чтобы оператор import
выполнял дополнительную работу, разбивая имя пакета на .
, как функция __import__()
, уже выполняет и перемещая имена, но это означало бы дублирование некоторых усилий. Таким образом, вместо этого реализация __import__()
возвращает самый правый модуль, а не самый левый , если и только если из списка передан, а не пуст.
(Синтаксис import pkg as p
и from pkg import mod as m
ничего не меняет об этой истории, за исключением того, какие локальные имена назначаются - функция __import__()
не видит ничего другого, когда используется as
, все это остается в import
.)
Ответ 2
Я все еще чувствую себя странно, когда читаю ответ, поэтому попробовал приведенные ниже примеры кода.
Сначала попробуйте построить ниже структуру файла:
tmpdir
|A
|__init__.py
| B.py
| C.py
Теперь A является package
, а B
или C
является module
. Поэтому, когда мы пытаемся использовать такой код в ipython:
Во-вторых, запустите пример кода в ipython:
In [2]: kk = __import__('A',fromlist=['B'])
In [3]: dir(kk)
Out[3]:
['B',
'__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'__path__']
Кажется, что fromlist работает так, как мы ожидали. Но вещи становятся проводными, когда мы пытаемся сделать то же самое на module
. Предположим, что в нем имеется модуль C.py и код:
handlers = {}
def hello():
print "hello"
test_list = []
Итак, теперь мы пытаемся сделать то же самое на нем.
In [1]: ls
C.py
In [2]: kk = __import__('C')
In [3]: dir(kk)
Out[3]:
['__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'handlers',
'hello',
'test_list']
Итак, когда мы просто хотим импортировать test_list, это работает?
In [1]: kk = __import__('C',fromlist=['test_list'])
In [2]: dir(kk)
Out[2]:
['__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'handlers',
'hello',
'test_list']
Как показывает результат, когда мы пытаемся использовать fromlist в module
, а не в package
, параметр fromlist не помогает вообще, потому что module
был скомпилирован. Когда он импортируется, нет возможности игнорировать другие.
Ответ 3
Ответ можно найти в документации для __import__
:
Список из списка должен быть списком имен для эмуляции from name import ...
или пустым списком для эмулирования import name
.
При импорте модуля из пакета обратите внимание, что __import__('A.B', ...)
возвращает пакет A, когда fromlist пуст, но его подмодуль B, когда fromlist не пуст.
Итак, в основном, как работает реализация __import__
: если вы хотите подмодуль, вы передаете fromlist
, содержащий что-то, что вы хотите импортировать из подмодуля, а реализация if __import__
такова, что подмодуль возвращается.
Дальнейшее объяснение
Я думаю, что семантика существует, так что возвращается наиболее релевантный модуль. Другими словами, скажем, у меня есть пакет foo
, содержащий модуль bar
с функцией baz
. Если I:
import foo.bar
Затем я называю baz
как
foo.bar.baz()
Это похоже на __import__("foo.bar", fromlist=[])
.
Если вместо этого я импортирую с помощью:
from foo import bar
Затем я называю baz
как bar.baz()
Что было бы похоже на __imoort__("foo.bar", fromlist=["something"])
.
Если я это сделаю:
from foo.bar import baz
Затем я называю baz
как
baz()
Это как __import__("foo.bar", fromlist=["baz"])
.
Итак, в первом случае мне нужно будет использовать полностью квалифицированное имя, поэтому __import__
возвращает имя первого модуля, которое вы будете использовать для ссылки на импортированные элементы: foo
. В последнем случае bar
- это наиболее специфический модуль, содержащий импортированные элементы, поэтому имеет смысл, что __import__
вернет модуль foo.bar
.
Второй случай немного странный, но я предполагаю, что он был написан таким образом, чтобы поддерживать импорт модуля с использованием синтаксиса from <package> import <module>
, и в этом случае bar
по-прежнему является наиболее конкретным модулем для возврата.