Отступы в Python — вариант решения
В подавляющем большинстве языков, если во всем исходном тексте программы убрать все отступы, а затем применить автоформатирование, то программа останется полностью рабочей и при этом станет оформлена в едином стиле.
Казалось бы, в случае с Python такая операция не возможна.
По крайней мере, я нигде не смог найти, как моментально обнаружить случайно смещенные отступы в Python. Пришлось решить эту проблему самому.
Вступление
Уважаемый читатель!
Если для тебя близко утверждение:
1) Что программирование — это высокое искусство.
2) Что при программировании на каком-либо языке нужно по возможности максимально использовать всю мощь и разнообразие этого языка для минимализации исходного кода.
3) Что при программировании нужно показать в исходном тексте программы свой высокий уровень, чтобы про твою программу ни кто не мог сказать, что она «тупая».
Если хотя бы один(!) из вышеперечисленных моментов тебе близок, то пожалуйста, не читай эту статью! Не трать, пожалуйста, на нее свое время.
В ней пойдет речь об одном совершенно надуманном и абсурдном моменте программирования.
Уважаемый читатель!
Если для тебя уже близко утверждение:
1) Что программирование — это рутинная, довольно однообразная работа, похожая просто на рытье бесконечной извилистой канавы.
2) Что при программировании на каком-либо языке нужно использовать определенный минимально-оптимальный набор команд языка (возможно отбросив большую часть его команд), чтобы в твоей программе легко(!) разобрался даже программист-юниор.
3) Что при программировании твоя программа должна быть в определенной степени «тупой». Чтобы после того, как ты ее внедрил, ты мог заняться новым проектом и спокойно поставить программиста-юниора, участвовавшего в проекте, на ее сопровождение и доработку под регулярные небольшие новые требования заказчика.
Если все эти три вышеперечисленные утверждения для тебя верны, то, возможно, у тебя присутствует проблема, решение которой я и хочу предложить.
Основные недостатки Python:
1) Python «не минимизирован» по использованию ресурсов и своих данных и, таким образом, не подходит для написания программ, требовательных к использованию ресурсов, таких как мобильные приложения, программы низкого уровня (драйвера, резидентные программы и т.п.) и т.д.
2) Python медленный и однопоточный ( GIL — Global Interpreter Lock ).
3) В Python программные блоки основаны ТОЛЬКО(!) на отступах. Из-за этого:
- уменьшается «читабильность» программ (см.ниже);
- невозможно полноценное автоформатирование исходного текста;
- появляется вероятность возникновения ошибки при случайном и незамеченном смещении отступов, которую иногда бывает очень трудно найти и исправить.
Второй недостаток Python решается тем, что у него прекрасная двусторонняя интероперабельность с C/C++.
Часто успешный проект развивается довольно стремительно. И сначала высоких требований к быстродействию нет, и в Python при необходимости используют крошечные вставки на С/С++.
Но по мере развития и расширения проекта, требования к быстродействию соответственно возрастают, и Python все чаще начинает выполнять функции вызываемого языка из С/С++. При этом, роль самого Python не уменьшается, т.к. при программировании участков, не требующих высокой скорости выполнения (а их обычно довольно много в крупных проектах), Python является более удобным инструментом, чем С/С++.
А для третьего недостатка Python я хотел бы предложить свой вариант решения.
Все вы знаете, что для подавляющего большинства языков очень часто используется автоформат исходного текста.
Т.е. с какими бы отступами ни была написана программа на этом языке, при запуске автоформатирования все отступы будут приведены к стандартному виду. Для Python такое кажется невозможным.
Любой программист, написавший проекты в несколько тысяч строк языка, знает, что процесс разработки — это не просто придумывание очередного куска программы и его наполнение, а постоянное перемещение участков кода то в подпрограмму, то между блоками программы, то в общие внешние модули и т.п.
Тем более, что на практике юзеры/заказчики уже на начальной стадии, поработав с первыми набросками программы, начинают менять и дополнять задачи конечного проекта, что приводит к сильной корректировке исходного кода.
И тогда в Python, в случае значительного изменения десятков кусков чужой(!) программы (или же своей, но которую уже совершенно не помнишь), стоит при переносе блока случайно зацепить лишний участок кода, принадлежащего другому блоку, то программа может остаться полностью работоспособной, но алгоритм ее работы поменяется (читай «программа начнет работать неправильно»), и найти место такой ошибки в чужой программе и потом правильно восстановить отступы иногда бывает очень непросто!
В этом случае можно, конечно, посмотреть исходный текст (до изменений), но если уже внес в этом месте много корректировок, то придется «отматывать» всю эту цепочку исправлений.
Решение проблемы отступов в Python
Отступы в Python формируются следующими командами:
И для удаления зависимости от отступов, для каждой из этих команд я решил взять за правило использовать «завершающую» команду, которая однозначно будет закрывать блок команд (отступов).
Команды «class» и «def»
Для команд class/def обычно нет проблемы с завершением блока отступов, т.к. блок закрывается новой командой class/def.
Единственный случай — это наличие одной или нескольких подпрограмм, объявленных внутри другой подпрограммы/метода.
Тогда, если случайно сместить блок команд последней внутренней подпрограммы, то он сольётся с командами подпрограммы/метода, в которой объявлена эта внутренняя подпрограмма.
В этом случае в конце этой внутренней подпрограммы/метода нужно просто поставить «return», что и будет четко обозначать окончание блока её команд.
Команды «for» и «while»
В конце оператора «for» и «while» нужно ставить «continue».
Т.е. команда будут выглядеть так:
Случайное удаление отступа в последних командах блока «for» не приводит к ошибке выполнения программы, но приводит к ошибочным результатам! И очень не просто найти такой сбой, если точно не знаешь алгоритм программы (например, если это программа уволившегося коллеги)!
А в нижеуказанном примере случайное удаление отступа сразу выдаст ошибку:
Команда «if»
В конце оператора «if» нужно ставить команду «elif 0: pass», а вместо «else» использовать команду «elif 1:».
Т.е. для «if» будет законченный блок команд:
Если оформить так, как показано выше, то в блоке команд «if… elif 0: pass» удаление отступа сформирует ошибку запуска.
Без «elif 0: pass», если потом случайно удалятся отступы в последних строках блока «if», то сначала будешь искать место, из-за которого программа неожиданно стала неправильно работать, а потом думать: какие отступы должны быть в блоке, а какие — нет.
Почему еще, на мой взгляд, желательно закрывать блоки команд, созданные операторами «for»,
«while», «if» и т.д…
Потому что, когда смотришь на участок кода, где присутствует большая разница в уровне
отступов между непосредственно концом текущего блока и началом следующего, то часто уже не можешь понять, какой отступ чему принадлежит.
Тогда конструкции с «continue» и «elif 0: pass», помимо защиты от случайного удаления, позволят также указать какой блок чем начат и написать комментарий к нему.
Например, видишь конец большого блока:
И сложно вспомнить, что значит каждый уровень отступов.
Но уже гораздо проще, когда это выглядит вот так:
Команда «try»
Тут полная аналогия с «if».
Единственная проблема — команда «finally:». После нее больше нельзя поставить ни одну из команд текущего try-блока.
Поэтому если есть необходимость в ее использовании, то для сохранения возможности автоформата и защиты от случайного удаления отступов нужно убрать весь блок команд после «finally:» в локальную подпрограмму (т.е. объявить её как подпрограмму внутри текущей подпрограммы).
Т.е. текст с «finally:» будет таким:
В этом случае тоже можно спокойно применить автоформатирование и не бояться
случайного удаления отступов.
Команда «with»
Для «with» в Python нет никаких дополнительных команд, которые могли бы послужить концом блока. Поэтому ситуация с «with» аналогична ситуации с «finally».
Т.е. либо мы все команды, выполняющиеся в блоке оператора «with», выносим в локальную подпрограмму, либо… А вот дальше я скажу жутко кощунственную вещь: «… либо просто не нужно его никогда использовать.»
Дело в том, что «with», с одной стороны, всего лишь «обертка» для существующих команд Python (т.е. ее всегда можно заменить аналогичным набором команд), с другой стороны, практика показала, что для программистов-юниоров этот контекстный менеджер трудноват для полноценного освоения. И поэтому, если хочешь, чтобы после тебя внедренный проект спокойно сопровождал программист-юниор, то не нужно в проекте использовать команды, затрудняющие его работу.
Заключение
Я думаю вы уже поняли, что если на Python написать программу с применением ВЕЗДЕ(!) вышеуказанных приемов оформления блоков отступов, то достаточно легко написать ПОЛНОЦЕННОЕ АВТОФОРМАТИРОВАНИЕ такой программы даже при «перекосе» или полном удалении отступов в ней, т.к. для всех блоков команд есть начало и конец блока, не зависящие от отступов.
А теперь с улыбкой поставим вопрос так: «Какие недостатки есть у Python, если правильно оформлять блоки отступов, при необходимости взаимодействовать с С/С++ и не применять Python в мобильных и ресурсокритичных приложениях?».
Ответ: «Только несущественные недостатки. Т.е. по большому счету — нет.»
И при такой постановке вопроса нам останется только наслаждаться основными достоинствами Python.
Подробно про отступы в Python
Отступы в Python используются для создания группы операторов. Многие популярные языки, такие как C и Java, используют фигурные скобки (<>) для определения блока кода, Python использует отступы.
Как работает отступ в Python?
При написании кода на Python мы должны определить группу операторов для функций и циклов. Это делается путем правильного отступа операторов для этого блока.
Начальные пробелы (пробел и табуляция) в начале строки используются для определения уровня отступа строки. Вы должны увеличить уровень отступа, чтобы сгруппировать операторы для этого блока кода. Аналогичным образом уменьшите отступ, чтобы закрыть группировку.
Обычно четыре пробела или один символ табуляции используются для создания или увеличения уровня отступа кода. Давайте посмотрим на пример, чтобы понять отступы кода и группировку операторов.

Правила отступов
- Мы не можем разделить отступ на несколько строк с помощью обратной косой черты.
- Первая строка кода Python не может иметь отступа, она вызовет IndentationError .
- Вам следует избегать смешивания табуляции и пробелов для создания отступов. Это потому, что текстовые редакторы в системах, отличных от Unix, ведут себя по-разному, и их смешивание может привести к неправильному отступу.
- Для отступа предпочтительнее использовать пробелы, чем символ табуляции.
- Лучше всего использовать 4 пробела для первого отступа, а затем продолжать добавлять 4 дополнительных пробела для увеличения отступа.
Преимущества
- В большинстве языков программирования отступы используются для правильной структуры кода. В Python он используется для группировки, автоматически делая код красивым.
- Правила отступов Python очень просты. Большинство IDE Python автоматически создают отступ для кода, поэтому очень легко написать код с правильным отступом.
Недостатки
- Поскольку для отступов используются пробелы, если код большой, а отступы повреждены, исправлять его очень утомительно. В основном это происходит при копировании кода из онлайн-источников, документа Word или файлов PDF.
- Большинство популярных языков программирования используют фигурные скобки для отступов, поэтому любому, кто приходит с другой стороны мира разработки, сначала трудно приспособиться к идее использования пробелов для отступов.
Примеры IndentationError
Давайте посмотрим на несколько примеров ошибки IndentationError в коде Python.
У нас не может быть отступа в первой строке кода. Вот почему возникает ошибка IndentationError.

Строки кода внутри блока if имеют другой уровень отступа, отсюда и ошибка IndentationError.

Здесь последний оператор печати имеет некоторый отступ, но нет оператора для его прикрепления, поэтому возникает ошибка отступа.
Вывод
Отступы делают наш код красивым. Он также служит для группировки операторов в блок кода. Это приводит к привычке писать красивый код все время.
Разметка кода Python, PEP 8
Последующие строки обернутых элементов в скобки должны быть выравнены либо по вертикали, либо с помощью висящего отступа.
При использовании висячего отступа, следует учитывать следующее: в первой строке не должно быть аргументов, и следует использовать дополнительный отступ, чтобы четко отличить себя как строку продолжения.
Правильно:
Не правильно:
Правило 4 пробелов не является обязательным для строк продолжения.
По желанию:
Когда условная часть if достаточно длинная (более 79 символов) и мы должны записать ее в несколько строк, стоит отметить, что сочетание двухсимвольного ключевого слова (например, if ), одного пробела и открывающей скобки, при переносе, создает естественный отступ в 4 символа для последующих строк. Это может привести к визуальному конфликту с дальнейшим набором кода, вложенным в конструкцию if, которое также будет иметь отступ в 4 пробела. Этот PEP не занимает явной позиции о том, как следует дополнительно визуально отличать такие строки от вложенного кода внутри конструкции if . Приемлемые варианты в этой ситуации можно посмотреть в примерах ниже, но не ограничиваться этим:
Закрывающая скобка в многострочных конструкциях может располагаться либо под первым символом последней строки списка:
или может быть на уровне первого символа строки, которая начинает многострочную конструкцию:
TAB или пробелы?
Пробелы являются предпочтительным методом отступа.
Табуляция должна использоваться исключительно для соответствия с кодом, который уже имеет такие отступы.
Python 3 запрещает смешивать использование табуляции и пробелов для отступа. Код с отступом в виде комбинации символов табуляции и пробелов должен быть преобразован исключительно в пробелы.
При вызове интерпретатора командной строки Python 2 с параметром -t выдает предупреждения о коде, который смешивает табуляции и пробелы. При использовании -tt эти предупреждения становятся ошибками. Эти варианты настоятельно рекомендуются!
Максимальная длина строки с кодом:
Ограничьте все строки максимум 79 символами.
Для строк документации или комментариев длина строки должна быть ограничена 72 символами.
Ограничение ширины окна редактора позволяет одновременно открывать несколько файлов и хорошо работает при использовании инструментов обзора, которые представляют две версии кода в соседних окнах.
Перенос по умолчанию в большинстве редакторов нарушает визуальную структуру кода, что делает его более трудным для понимания. Данные ограничения приняты для того, чтобы избежать автоматического переноса строк в редакторах с шириной окна, установленной на 80 символов, даже если он помещает маркер курсора в последний столбец. Некоторые IDE могут вообще не иметь авто-переноса строк.
Некоторые команды разработчиков предпочитают более длинные строки при написании кода. Исключительно для поддержания такого кода внутри команды, разрешается увеличить ограничение длины строки до 99 символов, при условии, что комментарии и документация должна быть ограничена 72 символами
Стандартная библиотека Python консервативна и требует ограничения строки до 79 символов (и строки документации/комментариев до 72).
Предпочтительный способ переноса длинных строк — это использование показанного выше переноса кода внутри скобок. Длинные строки можно разбить на несколько строк, заключив выражения в скобки.
Если такое не возможно, например, длинные строковые параметры в аргументах функций, то допустимо использование обратного слеша \ .
Еще один такой случай возможен с assert утверждениями.
Удостоверьтесь, что сделали отступ в 4 пробела для продолжения строки кода.
Перенос строки до или после двоичного оператора?
В течение десятилетий рекомендуется переносить строки после двоичного оператора. Но это может усложнить читабельность. При таком переносе, операторы, как правило, разбросаны по разным столбцам, при этом каждый оператор отошел от своей переменной и перешел на предыдущую строку. Здесь глаз должен сделать дополнительную работу, чтобы увидеть, какие элементы добавляются, а какие вычитаются:
Чтобы решить проблему читаемости, математики следуют противоположному соглашению. Следуя традиции математики, обычно получается более читаемый код:
В коде Python допускается перенос до или после двоичного оператора, если есть такое соглашение. Для нового кода предлагается математический стиль.
Пустые строки:
Определения функций и классов верхнего уровня должны быть заключены в две пустые строки.
Определения методов внутри класса заключены в одну пустую строку.
Дополнительные пустые строки могут использоваться для разделения групп связанных функций. Пустые строки могут быть пропущены между связкой связанных строк (например, набором фиктивных реализаций).
Используйте пустые строки в функциях, чтобы указать логические разделы.
Python принимает символ перевода формы control-L (т.е. ^ L ) в качестве пробела. Многие инструменты обрабатывают эти символы как разделители страниц, поэтому, вы можете использовать их для разделения страниц связанных разделов вашего файла. Обратите внимание, что некоторые редакторы и IDE могут не распознавать control-L .
Кодировка файла с кодом:
Код в основном дистрибутиве Python всегда должен использовать UTF-8 (или ASCII в Python 2).
Файлы, использующие ASCII (в Python 2) или UTF-8 (в Python 3), не должны иметь декларации кодировки.
В стандартной библиотеке, кодировки, отличные от заданных по умолчанию, следует использовать только для целей тестирования или в тех случаях, когда в комментариях или строке документации необходимо упомянуть имя автора, содержащее символы, отличные от ASCII. В противном случае использование \x , \u , \U или \N escape-файлов является предпочтительным способом включения данных не-ASCII в строковые литералы.
Для Python 3.0 и выше, для стандартной библиотеки предписана следующая политика. Все идентификаторы в стандартной библиотеке Python ДОЛЖНЫ использовать идентификаторы только ASCII и ДОЛЖНЫ использовать английские слова везде, где это возможно (сокращения и технические термины, которые не являются английскими). Кроме того, все строковые литералы и комментарии также должны быть в ASCII.
Единственными исключениями являются:
- Контрольные примеры, тестирующие функции, отличные от ASCII,
- Имена авторов. Авторы, чьи имена не основаны на латинском алфавите, ДОЛЖНЫ обеспечить транслитерацию своих имен.
Проектам с открытым исходным кодом с глобальной аудиторией рекомендуется придерживаться аналогичной политики.
Импорт:
Импорт обычно должен быть в отдельных строках:
Это нормально, хотя:
Импорт всегда помещается вверху файла, сразу после любых комментариев и строк документации, а также перед глобальными переменными и константами модуля.
Импорт должен быть сгруппирован в следующем порядке:
- Импорт стандартной библиотеки.
- Связанный импорт третьей стороны.
- Локальный импорт приложений или библиотек.
Вы должны поставить пустую строку между каждой группой импорта.
Рекомендуется абсолютный импорт, так как он обычно более читабелен и, как правило, ведет себя лучше (или, по крайней мере, дает лучшие сообщения об ошибках), если система импорта настроена неправильно (например, когда каталог внутри пакета заканчивается в sys.path ):
Однако явный относительный импорт является приемлемой альтернативой абсолютному импорту, особенно когда речь идет о сложном макете в пакете, где использование абсолютного импорта было бы излишне многословным:
Код стандартной библиотеки должен избегать сложных макетов пакетов и всегда использовать абсолютный импорт.
Неявный относительный импорт никогда не должен использоваться и был удален в Python 3.
При импорте класса из модуля, обычно можно записать следующее:
Если это написание вызывает локальные конфликты имен, то запишите импорт через точку:
и используйте myclass.MyClass и foo.bar.yourclass.YourClass .
Следует избегать импорта подстановочных знаков ( from <module> import * ), так как из-за этого неясно, какие имена присутствуют в пространстве имен, запутывает как читателей, так и многие автоматизированные инструменты. Существует один оправданный вариант использования для импорта с использованием подстановочного знака, который заключается в повторной публикации внутреннего интерфейса как части общедоступного API.
При повторной публикации имен, все же применяются приведенные ниже рекомендации, касающиеся открытых и внутренних интерфейсов.
Расположение имен "dunders" в коде:
Имена "dunders" (имена с двумя начальными и двумя замыкающими подчеркиваниями), такие как __all__ , __author__ , __version__ и т. ., Должны быть помещены после строки документации модуля, но перед любыми операторами импорта, кроме __future__ . Python предписывает, что в __future__ импорт должен стоять перед любым другим кодом, кроме строк документации:
Кавычки в строках:
В Python одинарные и двойные кавычки функционально одинаковы. PEP не дает рекомендации какие из них предпочтительнее. Выберите правило и придерживайтесь его. Если, например, строка содержит одинарные кавычки, чтобы избежать обратной косой черты в строке, используйте дополнительно двойные кавычки. Это улучшает читаемость.
Для строк с тройными кавычками всегда используйте символы двойной кавычки, чтобы соответствовать соглашению с документированной строкой в PEP 257.