Почему Python __import__ требует fromlist? - PullRequest
73 голосов
/ 27 апреля 2010

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

module = __import__('module_name')

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

module = __import__('module_name.submodule')

Конечно, это не работает; Вы просто получаете module_name снова. Вы должны сделать:

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

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

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

Ответы [ 3 ]

122 голосов
/ 28 апреля 2010

На самом деле, поведение __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__() возвращает самый правый модуль вместо самый левый один тогда и только тогда, когда из списка передан, а не пустой.

(Синтаксис import pkg as p и from pkg import mod as m ничего не меняет в этой истории, кроме того, какие локальные имена назначены - функция __import__() не видит ничего другого при использовании as, все остается в import реализация оператора.)

5 голосов
/ 10 апреля 2013

Я все еще чувствую себя странно, когда читаю ответ, поэтому я попробовал приведенные ниже примеры кода.

Сначала попробуйте создать структуру файла ниже:

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 скомпилировано. После того, как он импортирован, нельзя игнорировать другие.

2 голосов
/ 27 апреля 2010

Ответ можно найти в документации к __import__:

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

...