Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

Неявный импорт в python

#python #python_3x


Не могу разобраться в системе импортов.
Почему когда я делаю:

import os.path


то мне доступен os.walk, который находится в os, хотя я явно не импортирую os? Почему-то
со своими package такой финт не прокатывает. За счет чего это происходит?

upd 

Я понимаю, что импорт вносит импортируемое в пространство имен модуля. 
поэтому определив модуль 'myutils.py' c

import requests


в другом модуле получается следущая картина 

import myutils
resp=myutils.requests.get('http://ya.ru') # работает
resp2=requests.get('http://ya.ru') #  NameError: name 'requests' is not defined


Есть какая то особенность в механизме import os в os.path? Я понимаю, что os.path
есть часть os, а не 2 абсолютно отдельных package, но хочу точно понять механизм.
    


Ответы

Ответ 1



Импорт в Питоне объединяет две операции: найти (загрузить и проинициализировать) модуль ввести новые имена в текущем окружении (как операция присваивания =) На этапе поиска (пункт №1)—из спецификации import-конструкции: This name will be used in various phases of the import search, and it may be the dotted path to a submodule, e.g. foo.bar.baz. In this case, Python first tries to import foo, then foo.bar, and finally foo.bar.baz. If any of the intermediate imports fail, an ImportError is raised. то есть import a.b.c импортирует модули a, a.b, a.b.c и более вложенный модуль не может быть импортирован успешно, если импорт неудачен для любого модуля выше. На этапе присвоения имён (пункт №2): If the module being imported is not a top level module, then the name of the top level package that contains the module is bound in the local namespace as a reference to the top level package. The imported module must be accessed using its full qualified name rather than directly import a.b.c в Питоне делает доступным a имя в текущем пространстве имён (например, в глобальном пространстве имён модуля, который импортирует a.b.c) и присваивает атрибут: a.b и атрибут атрибута: a.b.c соответствующим загруженным модулям (аналог: a = sys.modules['a']; a.b = sys.modules['a.b']; ..). Хотя вопрос с подводхом, потому что os не является Питон-пакетом (__path__ атрибут не установлен) и os.path является обычным атрибутом (таким же как os.walk) с той разницей, что os.path является модулем и os.py содержит хак: sys.modules['os.path'] = path (что разрешает import os.path конструкцию). import os сам по себе уже делает os.path доступным без import os.path. Перефразируя вопрос, используя более регулярный пример: Почему когда я делаю: import os.pathimport html.parser то мне доступен os.walk html.escape, который находится в oshtml, хотя я явно не импортирую os html? import html.parser импортирует как html так и (естественно) html.parser модули и так как html/__init__.py (выполняемый на этапе импорта html) определяет escape функцию, то html.escape() также доступна как если бы мы просто выполнили import html. Почему-то со своими package такой финт не прокатывает. За счет чего это происходит? Если module.name после import module не работает, то это значит что ваш module/__init__.py не определяет name. В сторону: работоспособность module.name не зависит от __all__. __all__ документирует какие имена являются публичными (формально имена доступные по from module import *), чтобы не добавлять к каждому имени, которое не является частью интерфейса, _ в начале (имена типа _name по умолчанию исключены из from module import *). Но это не запрещает явное обращение такое как module._name (этого следует избегать, но если очень хочется, то можно). В сторону: не используйте from module import * вне REPL или вне __init__.py файла (пример оправданного использования: asyncio/__init__.py—asyncio предоставляет "плоский" публичный интерфейс (имена доступны прямо как asyncio.name), не смотря на то что реализация распределена по многочисленным вложенным модулям).

Ответ 2



Импортировать mypackage.mymodule_XXX и использовать mypackage.mymodule_YYY вы сможете только в том случае, если в файле __init__.py вашего пакета вы явно импортируете mymodule_YYY. Допустим у вас есть пакет mypackage: `/mypackage/` __init__.py mymodule_XXX.py mymodule_YYY.py __init__.py: from . import mymodule_XXX from . import mymodule_YYY Тогда вы cможете в вашей программе сделать так, как хочется: import mypackage.mymodule_XXX # здесь можно смело использовать mypackage.mymodule_YYY print(mypackage.mymodule_YYY) Что касается myutils. Импортируя модуль, вы просто вводите в пространство имен вашей программы переменную myutils, переменную requests вы не вводите, поэтому логично, что она не определена. Переменная же myutils, которая является модулем и имеет свое пространство имен, имеет переменную requests, поэтому через myutils.requests вы можете получить к ней доступ.

Ответ 3



print(len(globals()), globals().keys()) import os.path print(len(globals()), globals().keys()) когда вы импортируете os.path, на самом деле происходит импорт os, потом видимо у os просто запрашивается атрибут path, те импорт уже не происходит 8 dict_keys(['__name__', '__doc__', '__package__', '__cached__', '__spec__', '__builtins__', '__loader__', '__file__']) 9 dict_keys(['__name__', '__doc__', '__package__', '__cached__', '__spec__', 'os', '__builtins__', '__loader__', '__file__']) правильно будет from os import path

Комментариев нет:

Отправить комментарий