Казалось бы в случае с Python такая операция не возможна.
По крайней мере я ни где решения такого полноценного автоформатирования для Python не смог найти. Пришлось решить эту проблему самому.
Вступление
Дальше буду использовать сокращения: ПГ — программа, ПГ-ст — программист, ППГ — подпрограмма (функция/метод) и т.п.
Основные недостатки Python:
1) Python «не минимизирован» по использованию ресурсов и своих данных и т.о. не подходит для написания ПГ, требовательных к использованию ресурсов, таких как мобильные приложения, ПГ низкого уровня (драйвера, резидентные ПГ и т.п.) и т.д.
2) Python медленный и однопоточный ( GIL — Global Interpreter Lock ).
3) В Python программные блоки основаны ТОЛЬКО(!) на отступах. Из-за этого:
- уменьшается «читабильность» ПГ (см.ниже),
- невозможно полноценное автоформатирование исходного текста,
- появляется вероятность иногда очень трудно исправляемых ошибок при случайном и незамеченном ошибочном смещении отступов.
Первый недостаток Python сгладить сложно и тут он просто имеет ограничение в сфере своего использования. Но это естественный момент, т.к. невозможно придумать язык, который был бы максимально эффективен во всех сферах выполняемых задач.
Второй недостаток Python решается тем, что у него прекрасная двусторонняя
интероперабельность с C/C++.
Часто успешный проект развивается довольно стремительно. И сначала сильных требований к быстродействию нет и в Python при необходимости вставляют крошечные вставки на С/С++.
Но по мере расширения использования проекта требования к быстродействию быстро растут и Python все больше начинает выполнять функции вызываемого языка из С/С++. При этом значимость Python в проекте не падает, т.к. он остается более удобным инструментом чем С/С++ при ПГ-нии участков, не требующих большой скорости, которых в большом проекте все-равно много.
На третьем недостатке Python хочу остановиться подробнее.
Все вы знаете, для подавляющего большинства языков очень часто используется автоформат исходного текста.
Т.е. в каком бы стиле и с какими бы отступами ПГ-ст не написал ПГ-му, запускается функция автоформатирования исходного текста, которая приведет весь исходный текст к единому стилю. Для Python казалось бы такое не возможно.
Любой ПГ-ст, написавший проекты в несколько тысяч строк языка, знает, что процесс разработки — это часто не просто придумывание очередного куска ПГ и его набивка, а постоянный «перенос» кусков ПГ-мы то в ППГ, то в общие внешние модули, то между блоками кода.
Тем более, что на практике юзера/заказчики уже на стадии проб готового «скелета» проекта, когда они «потрогают» реальные наброски ПГ-мы, часто начинают довольно значительно «дополнять» постановку задачи, что приводит к сильной корректировке исходного кода.
И тогда в Python в случае сильного редактирования куска чужой(!) (или своей, но которую уже совершенно не помнишь) ПГ, если случайно «зацепил» ( и не заметил этого) при сдвиге/переносе блока лишний участок, который принадлежит другому блоку, то ПГ может остаться полностью работоспособной, но алгоритм ее работы поменяется (читай «ПГ начнет работать не правильно») и найти место такой ошибки в чужой(!) ПГ и потом правильно(!) восстановить отступы иногда бывает очень не просто!
В этом случае можно конечно исходный текст (до изменений) посмотреть, но если уже внес в этом месте много изменений, то придется еще и всю эту цепочку изменений «отматывать».
А вариант, как я для себя убрал третий недостаток я и хочу предложить Вашему вниманию.
Решение проблемы отступов в Python
Отступы в Python формируются следующими командами:
- class - def - for - while - if - try
И для удаления зависимости от отступов, для каждой из этих команд я решил взять за правило использовать «закрывающую» команду, которая однозначно будет закрывать блок команд (отступов).
Команды «class» и «def»
Для команд class/def обычно проблемы с завершением блока отступов нет, т.к. блок завершается новой командой class/def.
Единственный случай — это когда есть одна или несколько ППГ, объявленных внутри другой ППГ/метода.
def ppg_1(): def ppg_2(): ... команды ppg_2 ... def ppg_3(): ... команды ppg_3 ... ... команды ppg_3 ... ... команды ppg_3 ... ... команды ppg_1 ...
Тогда блок команд последней из этих внутренних ППГ может слиться с командами ППГ/метода, в которой объявлена эта внутренняя ППГ, если случайно ошибочно сместить этот блок команд последней внутренней ППГ.
def ppg_1(): def ppg_2(): ... команды ppg_2 ... def ppg_3(): ... команды ppg_3 ... ... команды ppg_3 ... ... команды ppg_3 ... ... команды ppg_1 ...
Но в этом случае в конце этой внутренней ППГ/метода нужно просто поставить «return» и
это и будет точно конец ППГ/метода.
def ppg_1(): def ppg_2(): ... команды ppg_2 ... def ppg_3(): ... команды ppg_3 ... ... команды ppg_3 ... ... команды ppg_3 ... return ... команды ppg_1 ...
Команды «for» и «while»
В конце оператора «for» и «while» нужно ставить «continue».
Т.е. команда будут выглядеть так:
for <...> : # начало блока ... команды .... continue # конец блока
и
while <...> : # начало блока ... команды .... continue # конец блока
Например, возьмем кусок исходного кода:
f = open(imya_file,'r',encoding='utf-8') i = 0 for line in f: #print(line) tek_str = line.rstrip('\n') #Уберем \n на концах строки tek_spisok = tek_str.split('\t') #Разбивка строк в список REGP = tek_spisok[0] DATA = tek_spisok[1] NNMT = tek_spisok[2] tb0.setItem( i, 0, QtWidgets.QTableWidgetItem(REGP) ) tb0.setItem( i, 1, QtWidgets.QTableWidgetItem(DATA) ) tb0.setItem( i, 2, QtWidgets.QTableWidgetItem(NNMT) ) i += 1 f.close()
Случайное удаление отступа в последних командах блока «for» не приводит к ошибке выполнения программы, но приводит к ошибочным результатам! И не просто искать что случилось, если не знаешь точно алгоритм ПГ (например, если это ПГ уволившегося коллеги)!
Вот как здесь:
f = open(imya_file,'r',encoding='utf-8') i = 0 for line in f: #print(line) tek_str = line.rstrip('\n') #Уберем \n на концах строки tek_spisok = tek_str.split('\t') #Разбивка строк в список REGP = tek_spisok[0] DATA = tek_spisok[1] NNMT = tek_spisok[2] tb0.setItem( i, 0, QtWidgets.QTableWidgetItem(REGP) ) tb0.setItem( i, 1, QtWidgets.QTableWidgetItem(DATA) ) tb0.setItem( i, 2, QtWidgets.QTableWidgetItem(NNMT) ) i += 1 f.close()
А в нижеуказанном случае случайное удаление отступа сразу выдаст ошибку:
f = open(imya_file,'r',encoding='utf-8') i = 0 for line in f: #print(line) tek_str = line.rstrip('\n') #Уберем \n на концах строки tek_spisok = tek_str.split('\t') #Разбивка строк в список REGP = tek_spisok[0] DATA = tek_spisok[1] NNMT = tek_spisok[2] tb0.setItem( i, 0, QtWidgets.QTableWidgetItem(REGP) ) tb0.setItem( i, 1, QtWidgets.QTableWidgetItem(DATA) ) tb0.setItem( i, 2, QtWidgets.QTableWidgetItem(NNMT) ) i += 1 continue f.close()
Команда «if»
В конце оператора «if» нужно ставить команду «elif 0: pass», а вместо «else» использовать команду «elif 1:».
Т.е. для «if» будет законченный блок команд:
if <> # начало блока ... команды .... elif <> ... команды .... elif 1: # вместо "else" ... команды .... elif 0: pass # конец блока
Например:
ret = 0 find_port = seek_port() if find_port != 'yes': #Выведем ошибку,что сканер не найден okno_info( 'Сканер не найден', font_medium, bg_txt) ret = 1 Dialog_scan.close() elif 0: pass QR_scan_main( 'Ряды', 1 )
Если так, как выше оформить, то в блоке команд «if… elif 0: pass» удаление отступа сформирует ошибку запуска.
Без «elif 0: pass» если потом случайно удалятся отступы в последних строках блока «if», то сначала будешь искать место, из-за которого ПГ неожиданно стала не правильно работать, а потом думать: какие отступы д.б. в блоке, а какие — нет.
ret = 0 find_port = seek_port() if find_port != 'yes': #Выведем ошибку,что сканер не найден okno_info( 'Сканер не найден', font_medium, bg_txt) ret = 1 Dialog_scan.close() QR_scan_main( 'Ряды', 1 )
Почему еще на мой взгляд желательно закрывать блоки команд, созданные операторами «for»,
«while», «if» b т.д…
Потому что, когда смотришь на конец завершающихся отступов, но при этом не видишь, где они начинаются, то уже часто не помнишь, какой отступ чем создан.
Тогда конструкции с «continue» и «elif 0: pass» еще (кроме защиты от случайного удаления отступов) позволят указать какой блок чем начат и написать комментарий к нему.
Например, видишь конец большого блока:
#print("{0}\t{1}".format(key,slovar[key]),end="\n") zn = str("{0}\t{1}".format(key,slovar[key])) new_zn = zn.replace("\\r\\n'","") itog_zn = itog_zn + new_zn[2:] + "\n" if n == 1: itog_str = tek_polya + str(itog_zn) f.write(str(itog_str)) itog_zn = '' elif n == 10 or len(slovar) == n: itog_str = str(itog_zn) f.write(str(itog_str)) itog_zn = '' n += 1 f.close() #Копируем файл txt в bak shutil.copyfile(kat+"\\"+imya_f+'.txt', kat+"\\"+imya_f+'.bak') #Выведем информационное сообщение self.okno_info( 'Вы отсканировали '+str(len(slovar))+ ' коробки(ок) в '+str(kych)+' ряд(ах)', self.font_medium, self.bg_txt) self.Dialog_scan.close() elif res == 'Выйти(ESC)': self.Menu.close() self.Dialog_scan.close()
И сложно вспомнить, что значит каждый уровень отступов.
Но уже гораздо проще, когда видишь вот так:
#print("{0}\t{1}".format(key,slovar[key]),end="\n") zn = str("{0}\t{1}".format(key,slovar[key])) new_zn = zn.replace("\\r\\n'","") itog_zn = itog_zn + new_zn[2:] + "\n" if n == 1: itog_str = tek_polya + str(itog_zn) f.write(str(itog_str)) itog_zn = '' elif n == 10 or len(slovar) == n: itog_str = str(itog_zn) f.write(str(itog_str)) itog_zn = '' elif 0: pass n += 1 continue # убираем символов b'код\\r\\n' по словарю f.close() #Копируем файл txt в bak shutil.copyfile(kat+"\\"+imya_f+'.txt', kat+"\\"+imya_f+'.bak') #Выведем информационное сообщение self.okno_info( 'Вы отсканировали '+str(len(slovar))+ ' коробки(ок) в '+str(kych)+' ряд(ах)', self.font_medium, self.bg_txt) self.Dialog_scan.close() elif 0: pass # варианты "закончили ряд" или "весь товар" elif 0: pass # Если был спец.коды - обрабатываем continue # цикл по сканированию кодов continue # цикл бесконеч., выход - когда "весь товар" elif res == 'Выйти(ESC)': self.Menu.close() self.Dialog_scan.close()
Команда «try»
Тут полная аналогия с «if».
try: # начало блока ... команды .... except <...>: ... команды .... except 1: # вместо "else" ... команды .... except 0: pass # конец блока
Единственная проблема — команда «finally:». После нее больше нельзя поставить
ни одну их команд текущего try-блока.
Поэтому если есть сильная необходимость в ее использовании, то для сохранения возможности автоформата и защиты от случайного удаления отступов, нужно убрать весь блок команд после «finally:» в ППГ и эту ППГ объявить как ППГ внутри текущей ППГ.
Т.е. текст с «finally:» будет таким:
def my_ppg(): ... return ... finally: my_ppg() ...
В этом случае тоже можно спокойно применить автоформатирование и не бояться
случайного удаления отступов.
Также, теперь я думаю, вы уже поняли, что если на Python написать ПГ с применением ВЕЗДЕ(!) вышеуказанных приемов оформления блоков отступов, то легко написать ПОЛНОЦЕННОЕ АВТОФОРМАТИРОВАНИЕ такой ПГ даже при «перекосе» или полном удалении отступов в ней, т.к. для всех блоков команд есть начало и конец блока, не зависящие от отступов.
Заключение
А теперь с улыбкой поставим вопрос так: «Какие недостатки есть у Python, если правильно оформлять блоки отступов, при необходимости писать для него на С/С++ и не применять в мобильных и ресурсокритичных приложениях ?».
Ответ: «Только несущественные недостатки. Т.е. по большому счету — нет.»
И при такой постановке вопроса нам останется только наслаждаться основными достоинствами Python.
- Простота.
- Минимальный цикл тестирования участков: написал-запустил-проверил.
- Мощность — библиотеки/фреймворки для Python есть «на любой вкус и цвет».
Эти три кита вместе дают, на мой взгляд, практически идеальный вариант языка (не забываем, что при условии вышеуказанной постановки вопроса!).
ссылка на оригинал статьи https://habr.com/ru/post/501538/
Добавить комментарий