В Python, если Вы хотите программно импортировать модуль, можно сделать:
module = __import__('module_name')
Если бы Вы хотите импортировать подмодуль, Вы думали бы, что это был бы простой вопрос:
module = __import__('module_name.submodule')
Конечно, это не работает; Вы просто добираетесь module_name
снова. Необходимо сделать:
module = __import__('module_name.submodule', fromlist=['blah'])
Почему? Фактическое значение fromlist
кажется, не имею значения вообще, пока это непусто. Какой смысл того, чтобы требовать аргумента, затем игнорируя его значения?
Большая часть материала в Python, кажется, сделана на серьезном основании, но ни за что в жизни, я не могу придумать разумное объяснение этого поведения для существования.
Фактически, поведение __ 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
должен назначить "крайний левый" объект модуля самому "левому" имени: 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 __ ()
вернуть крайний правый модуль вместо крайнего левого модуля тогда и только тогда, когда fromlist передан и не пуст.
(Синтаксис import pkg as p
и из pkg import mod as m
не меняет ничего в этой истории, кроме того, какие локальные имена присваиваются - Функция __import __ ()
не видит ничего нового при использовании как
, все остается в реализации оператора import
.)
Ответ можно найти в документации для __ import __
:
fromlist должен быть списком имён для имитации
from name import ...
или пустым списком для имитацииimport name
.При импорте модуля из пакета обратите внимание, что
__ import __ ('A.B', ...)
возвращает пакет A, когда fromlist пуст, но его подмодуль B, когда fromlist не пуст.
По сути, именно так и работает реализация __ import __
: если вам нужен подмодуль, вы передаете fromlist
, содержащий то, что вы хотите импортировать из подмодуля, и реализацию если __ import __
таково, что возвращается подмодуль.
Дальнейшее объяснение
Я думаю, что семантика существует, поэтому возвращается наиболее релевантный модуль. Другими словами, скажем, у меня есть пакет foo
, содержащий модуль bar
с функцией baz
. Если я:
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
, и в этом случае bar
по-прежнему является наиболее специфичным возвращаемым модулем.