Почему делает Python __, импортируют __, требуют fromlist?

В Python, если Вы хотите программно импортировать модуль, можно сделать:

module = __import__('module_name')

Если бы Вы хотите импортировать подмодуль, Вы думали бы, что это был бы простой вопрос:

module = __import__('module_name.submodule')

Конечно, это не работает; Вы просто добираетесь module_name снова. Необходимо сделать:

module = __import__('module_name.submodule', fromlist=['blah'])

Почему? Фактическое значение fromlist кажется, не имею значения вообще, пока это непусто. Какой смысл того, чтобы требовать аргумента, затем игнорируя его значения?

Большая часть материала в Python, кажется, сделана на серьезном основании, но ни за что в жизни, я не могу придумать разумное объяснение этого поведения для существования.

74
задан ieure 12 August 2014 в 15:33
поделиться

2 ответа

Фактически, поведение __ 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 .)

124
ответ дан 24 November 2019 в 11:59
поделиться

Ответ можно найти в документации для __ 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 import , и в этом случае bar по-прежнему является наиболее специфичным возвращаемым модулем.

2
ответ дан 24 November 2019 в 11:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: