Pull to refresh

Comments 14

Интересно, никогда не сталкивался с перекрытием имён.
Дальше вопрос логически следует — а как избежать перекрытия имен?

Мне кажется, решение не в том, чтоб детально разобраться, как работает импорт в 2.7 и потом следить, чтоб ненароком что-то не то не импортировать (например, выбирая безопасные названия для модулей).

Ну т.е. так вопрос решить можно, и его решают: например, если в какой-нибудь django-библиотеке foo шаблонные теги нужно загружать как {% load "foo_tags" %}, то вероятность 92.64%, что боролись с семантикой импортов (либо это cargo-cult, оставшийся от какой-то библиотеки, боровшейся с семантикой импортов).

Мне кажется, что все-же лучше проблему решить тем, что всегда писать

from __future__ import absolute_imports

в коде на 2.х (например, засунув эту строчку в шаблон для .py файла в IDE) и/или переходить на Python 3.x, где этот алгоритм импортирования уже по умолчанию.

Этот future-импорт убивает двух зайцев: код по семантике импортов становится как 3.х (а значит переход будет проще), и устраняется возможность сломать питоний код, просто создав рядом в папочке файл с неудачным именем. Кроме того, явное ведь лучше неявного — с absolute_imports относительные импорты нужно указывать явно.

С другими future-импортами есть проблема — их нужно писать сверху файла, и когда читаешь код, чтоб его правильно понять, нужно держать в уме, какие future-импорты действуют. С absolute_imports этой проблемы нет, т.к. во-первых, он влияет на импорты (а их тоже пишут сверху файла), а во-вторых, он меняет поведение, котрое в большинстве случаев и так было неявным или ошибочным.
Спасибо огромное за комментарий. Он помог обнаружить, что часть статьи оказалась неопубликованной в результате ошибки во время форматирования. А именно вот это:
В случае возникновения подобных коллизий имен возможны следующие решения:
  1. Переименовать модуль, либо пакет таким образом, чтобы имя пакета не совпадало с именем модуля, т.е. исключить совпадение имен
  2. Иногда может помочь включение абсолютных путей по умолчанию для импорта с помощью инструкции from __future__ import absolute_import (на самом деле в этом случае, лишь повышается контроль за последовательностью поиска пакетов и модулей путем внесения изменений в sys.path)

Можно играть с sys.path перед импортами.
Сегодня потратил пару часов на подобную проблему, надо было читать хабру раньше :(

За статью спасибо!
Еще добавлю про импорты.
Неоднозначное поведение когда вызывается код из модуля из пэкэджа, в котором вызывается сабпэкэдж с модулями.
Как оказалось функция __import__ не выполняет магию и путь не добавляется в paths. Долго промучался, потом увидел, что во всех сабмодулях импорт идет напрямую и работает, заменил на путь от корня программы (был например пэкэдж admin внутри модуль show внутри этого модуля, для загрузки admin.submodules надо было писать именно так, а не как ожидаемо (просто submodules)).
Структура
plugins.py  - файл_с__import__
admin/__init__.py - ясно
admin/show.py - файл с импортом плагинов
admin/submodules/__init__.py - пэкэдж
admin/submodules/billing.py  - файл что надо импортнуть
Выходило примерно так
plugins.py
for i in ['admin.show','test.test']:
 __import__(i)


admin/show.py
import submodules
for i in submodules.__all__:
 __import__('submodules.'+i)

Результат был что-то вроде ImportError No module named billing
Importlib тоже подвержен этой проблеме.
Параметр package вниманием не обошли?
И так и так пробовал, нужно прописывать точно путь, тогда работает.
>>> import ctypes
>>> ctypes.__package__
'ctypes'
>>> import importlib
>>> m = importlib.import_module('util', package=ctypes.__package__)
Traceback (most recent call last):
File "", line 1, in File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
ImportError: No module named util
>>> m = importlib.import_module('.util', package=ctypes.__package__)
>>> m.__name__
'ctypes.util'
>>>
Отличная статья, но для полноты было бы неплохо добавить про PYTHONHOME, PYTHONPATH и модули в зипах.
Sign up to leave a comment.

Articles