Как стать автором
Обновить

Комментарии 31

Немного противоречит PEP 20
Explicit is better than implicit.
Есть такое.

Поэтому пока вижу использование этой либки в основном в проектах с устоявшимися правилами именования. В этом случае это правило не будет нарушаться, поскольку автоматизация импортов станет просто реализацией уже существующего порядка (или таки будет нарушаться, но не так сильно, в зависимости от философской позиции :-) ).

Плюс, для быстрого написания одноразового кода тоже удобно.
Для pylint я отключил проверку ошибки E0602 — Undefined variable. Поскольку такие ошибки теперь ловятся при запуске (smart imports пишут для каких переменных не нашли импорты). В остальном вроде работает.

Определённые проблемы с линтерами и автодополнением в IDE я ожидаю. Буду их решать по мере поступления сообщений об ошибках.

Подозреваю, в mypy теперь целиком и полностью сломалась проверка типов, связанных с импортируемыми модулями (да и в pylint наверно тоже)

Коллега предложил улучшение, если либа не найдена идти в pypy и искать либу там, после чего устанавливать её.
Я думаю это некоторый перебор: потенциальная дырка в безопасности. Да и можно загрязнить левое окружение, если случайно не оттуда запустились.

С другой стороны, такое правило для импорта написать не сложно в виде отдельного плагина.

Основную проблему уже обозначили выше — такой подход ломает любой линтинг и автокомплит, что, собственно, безумно плохо.

Вообще, импорты добавляет IDE автоматически, поэтому никогда с ними долго не возился. Ну и когда импорты в открытом виде в файле, это наглядно, сразу видишь, что он использует.
Как в данном случае либа будет сочетаться с IDE?
Думаю, это будет зависеть от IDE и возможности пилить под неё плагины.
НЛО прилетело и опубликовало эту надпись здесь

А как решается проблема "откуда это взялось?" при чтении исходного кода?

Фиксированные правила импорта и продуманная структура проекта оставляют не много места для таких вопросов. Кроме того, всегда можно конфиг посмотреть.

Нет, это вы отвечаете на вопрос "как этот модуль тут появился". А у меня другой вопрос — "откуда это взялось"?


Если у меня в коде написано queue.Empty — это локальная переменная которую кто-то где-то инциализировал или это модуль? А если у меня есть переменная chunk и я забыл её проинциализировать, а вы мне любезно туда chunk модуль подсунули? За что?

Нет, это вы отвечаете на вопрос «как этот модуль тут появился». А у меня другой вопрос — «откуда это взялось»?

Приведите пример в котором ответ на ваш вопрос не даётся просмотром импортов. Пока я не понимаю что вы хотите сказать.

Если у меня в коде написано queue.Empty — это локальная переменная которую кто-то где-то инициализировал или это модуль? А если у меня есть переменная chunk и я забыл её проинициализировать, а вы мне любезно туда chunk модуль подсунули? За что?

Для этого в проекте должно совпасть несколько довольно критичных ошибок:

— Должно случиться пересечение имён модулей и переменных.
— Функциональность не должна быть покрыта ни одним тестом (либо тесты не запускаются).
— Должен быть написан кривой код.

Если такое случается чаще раза в год, то на проекте проблемы совсем другого рода и, действительно, играться с метапрограммированием команде не стоит.

Сейчас в Python идёт движение за то, чтобы модуль выглядел как обычный объект. Добавляются разные технические методы для эмулирования этого. Например есть PEP-0562 То есть с точки зрения кода должно быть без разницы что в переменной находится (модуль, функция, обычный объект), пока эта сущность предоставляет нужный интерфейс.

Плюс, разносить объявление переменной и её использование на несколько экранов (а именно в этом случае нельзя сказать что в ней) — моветон. Не надо так делать.

Как одной строчкой сломать/отключить работу в любой IDE autocomplete, linter'ов, функций рефакторинга, статических анализаторов типов и т.д. Список можно продолжать. Не говоря о невозможности использовать условные импорты.

Почему условные импорты невозможно использовать?

Из доступных правил не получится сделать так:


try:
    import loads from ujson as json_loads
except ImportError:
    import loads from json as json_loads
Не обязательно всё пихать в правила. Нужен условный импорт — делаем условный импорт.

import smart_imports

smart_imports.all()

try:
    from ujson import loads as json_loads
except ImportError:
    from json import loads as json_loads

json_data = '{"x": 1}'


parsed_data = json_loads(json_data)

parsed_data['y'] = math.pi

print(parsed_data)
# {'x': 1, 'y': 3.141592653589793}


А если очень надо, то и правило написать не сложно. Только оно не надо скорее всего, поскольку усовный импорт должен только в одном месте делаться.
Напоминает makeheaders для Си, только там всё же генерировался implicit header.
мне кажется, модулей слишком много. вот смотрю один из своих проектов на delphi — примерно 1.5. млн строк и 550 модулей.
я как-то пробовал отлаживать плюсовый код с примерно тысячей модулей. после открытого примерно 30-го файла по колл-стэку я окончательно потерялся.
То есть по ~2727 строк на файл (в среднем) учитывая разброс, должны быть и по 10 000 строк исходники. По-моему это немного перебор :-D

Касательно моего проекта. В вебе есть есть определённые соглашения по организации кода. Например, фреймворк Django рекомендует отдельно описывать схему базы, структуру url и прочее для каждого компонента. Это приводит к появлению файлов с небольшим количеством кода, но засчёт правильного разделения кода по модулям это в итоге удобно.
Полагаю, такая штука имела бы смысл, если сделать её полностью compile-time, с поддержкой от IDE. Я бы вообще просто сделал плагин к vscode, который делает то же самое, и обучает остальные линтеры считать импорты «совершёнными», и всё, можно работать
Как альтернатива — сделать один модуль который будет содержать все нужные импорты, и использовать:
from all_libs import *
Без магии и инструментарий не поломается.
Для малых проектов это прокатило бы. Для больших будет скорее геморойно:

— Циклические зависимости вылезут во всей красе.
— В случае компонентов, организованных по одному шаблону, нельзя будет обратиться к соседнему модулю по имени (так как оно пересечётся с таким же именем в другом компоненте). То есть вопрос импортов полностью не закроется.
— Время запуска будет максимльным из возможного, так как вообще всё придётся импортировать.
1. Циклические импорты можно решить через инициализацию (т.е. «проблемный» код переместить в ф-ю из глобальной части, и запускать когда все уже импортированно)
А как у вас оно решается, когда нужны 2 модуля которые цыклический друг от друга?

2. Такие импорты делать класический или по полному пути компонента, либо аналогично сделать локальный модуль импорта.

3. Если проект стартует целиком и эти модули импортируются в любом случае (монолит), то разницы не будет.
А если нужна возможность запуска кусками, то можно сделать «режимы запуска» где будет импортироваться не все.

Ещё как вариант не импортировать глобально, а как один модуль, и использовать, например, как libs.math.sqrt, тогда можно вообще сделать ленивую подгрузку модулей.

Так же можно комбинировать подходы, и ложить в общий импорт только то, что действительно используется часто, тогда кол-во импортов скоратится до минимума.

Кроме того есть и другие архитектурные решения, где минимум импортов в больших проектах.
Насколько я понимаю используя и этот подход в том числе вы понимажаете понятность кода для самого себя в том числе. Ну и куча ненужного в коде лежит в итоге. Думаю
from {что-то} import *
использовать вовсе плохая практика, хоть питон и позволяет, да.
Я думаю, что избыточное количество импортов указывает на то, что у проекта высокая связность классов, а у отдельных скриптов куча задач. Думаю правильное разделение, а не убер-библиотека, вот что нужно. А так это похоже на костыль, который скрывает основную проблему.

В комментариях пишут про недостатки и со многим могу согласиться, но все-таки это красиво! В качестве решения проблемы с тулами может быть можно сделать двухэтапный вариант, когда тул генерирует список импортов и сохраняет в файл, который уже явно импортируется далее по тексту. Тогда ругань тулов будет только на свежедобавленные импорты до первого запуска.

сначала сделали динамический язык
потом добавили «типизацию»
теперь сделаем неявный импорт
потом что добавим?

PyCharm же всё автоматически добавляет, зачем ещё какую-то магию городить?

Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации