Python: надежная защита от потери запятой в вертикальном списке строк

в 13:31, , рубрики: python, Блог компании Фаматек, Программирование, метки:

Python: надежная защита от потери запятой в вертикальном списке строк Списки строк в программах встречаются часто. Для удобства чтения их не менее часто форматируют вертикально, по одной строке. И есть в такой конструкции уязвимость — если при изменении списка потерять запятую между элементами, то многие языки просто склеют строки слева и справа от пропущенной запятой — в результате получится валидный с точки зрения языка список, в котором на один элемент меньше чем ожидается и один элемент имеет некорректное значение. Есть много способов профилактики этой проблемы, но недавно на stackoverflow мне показали настолько простой и надежный способ, что я просто не могу им не поделиться.

Демонстрация проблемы

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

languages = [
	"english",
	"russian",
	"italian" # Печалька. Такое иногда бывает.
	"spanish" ]

Если внимательно посмотреть, между строками «italian» и «spanish» пропущена запятая. Но при запуске такой программы ошибок не будет: Python просто склеит строки «italian» и «spanish», превратив наш список вот в это:

languages = [ "english", "russian", "italianspanish" ]

На практике такие опечатки встречатся не то чтобы очень часто — но к багам приводят знатным и долгоотлаживаемым.

Как бороться по-феншую

В соответствии c феншуем, данный ряд проблем необходимо отсекать статическими анализаторами кода типа lint в рамках автобилда. Но тут есть неприятный нюанс — pylint по умолчанию не считает пример выше ошибочным. Следовательно, придется его долго и муторно настраивать, потому как есть много вполне корректного кода, где строки склеиваются по делу и никаких запятых быть не должно. Плюс не у всех настроена связка pylint + autobuild, а поднимать полноценный continous integration с нуля только ради указанной проблемы не всегда с руки.

Как борются на практике

На данный момент есть два популярных способа борьбы с этой проблемой. Первый заключается в том, чтобы оканчивать каждую строку запятой, включая последнюю, а терминатор списка писать на отдельной строке. Это позволяет в большинстве случаев избежать проблем при копипасте строк и удалении строк:

languages = [
	"english",
	"russian",
	"italian",
	"spanish", # Если переместить этот элемент вверх, запятые не поломаются.
]

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

Второй способ заключается в тактической установке запятых не после элементов, а перед ними. Это вытраивает красиву вертикальную черту, в которой пропуски видны невооруженным глазом:

languages = [
	  "english"
	, "russian"
	, "italian"
	, "spanish"
]

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

Новый способ

Был подсказан гуру на stackoverflow. Не могу сказать что он особо красив или удобен — но он прост и надежен. При потере запятой случается ошибка выполнения скрипта. Способ заключется в окружении каждой строки круглыми скобками — это превращает epression типа строка в сложносоставной expression, который уже склеивать нельзя:

languages = [
	("english"),
	("russian"),
	("italian") # Теперь потерянная запятая - синтаксичекая ошибка.
	("spanish") ]

Вот такое неожиданное решение. Надеюсь, послужит кому-нибудь источником вдохновения. Приятных выходных, коллеги :)

Автор: eyeofhell

Поделиться

* - обязательные к заполнению поля