Продолжаем публикацию перевода серии статей о схожести и различии двух языков.
Сегодня поговорим о сериализации словарей, JSON, регулярках, об ошибках и исключениях.
Другие статьи в этой серии:
- Первая часть — приведение к типу, тернарный оператор, доступ к свойству по имени свойства, словари, списки, строки, конкатенация строк.
- Эта статья
- Часть третья: современные Python и JS: строковые шаблоны (f-строки), распаковка списков, лямбда-функции, итерации по спискам, генераторы, множества.
- Четвертая часть — аргументы функций, создание и работа с классами, наследование, геттеры-сеттеры и свойства класса.
JSON
При работе со многими API удобно сериализовать объекты в JSON-объекты для удобства передачи и последующего разбора.
В Питоне есть стандартный модуль json:
import json json_data = json.dumps(dictionary, indent=4) dictionary = json.loads(json_data)
Здесь мы форматируем JSON с помощью отступов в 4 пробела.
В JS существует объект JSON с методами для создания и парсинга JSON-строк:
json_data = JSON.stringify(dictionary, null, 4); dictionary = JSON.parse(json_data);
Разбираем строки регулярками
В прошлой статье мы соединяли несколько строк в одну. Но как разделить одну длинную строку на несколько, особенно если разделителем выступает не один символ типа запятой, а целый диапазон различных вариантов? Здесь нам на помощь приходят регулярные выражения и метод split().
В Питоне метод split() относится к шаблону регулярных выражений. Вот как можно разделить текстовую строку на предложения по знакам препинания:
import re # Один или более символов "!?." за которыми следует ноль или более пробельных символов delimiter = re.compile(r'[!?\.]+\s*') text = "Hello!!! What's new? Follow me." sentences = delimiter.split(text) # sentences == ['Hello', "What's new", 'Follow me', '']
В JS метод split() относится к строкам:
// Один или более символов "!?." за которыми следует ноль или более пробельных символов delimiter = /[!?\.]+\s*/; text = "Hello!!! What's new? Follow me."; sentences = text.split(delimiter) // sentences === ["Hello", "What's new", "Follow me", ""]
Поиск регулярками по шаблону
Регулярные выражения часто используются для валидации данных из форм.
К примеру, для проверки корректности веденного e-mail адреса его надо сопоставить с шаблоном регулярного выражения.
что проверка адреса регулярками задача нетривиальная и несколько сложнее приведенного в этой статье метода
В Питоне это будет выглядеть примерно так:
import re # name, "@", and domain pattern = re.compile(r'([\w.+\-]+)@([\w\-]+\.[\w\-.]+)') match = pattern.match('hi@example.com') # match.group(0) == 'hi@example.com' # match.group(1) == 'hi' # match.group(2) == 'example.com'
Если участок текста совпадает с шаблоном, то при помощи метода group() возвращается совпавший участок целиком в котором можно выделить отдельные группы определенные в шаблоне.
0 — совпавшая (под)строка целиком, 1 — первая группа, 2 — вторая и т.д.
Если совпадений не найдено, вернется объект типа None.
В JS существует строковый метод match()который возвращает либо совпавший участок строки либо null.
// name, "@", and domain pattern = /([\w.+\-]+)@([\w\-]+\.[\w\-.]+)/; match = 'hi@example.com'.match(pattern); // match[0] === 'hi@example.com' // match[1] === 'hi' // match[2] === 'example.com'
В JS совпавший объект выглядит как массив. Элемент с индексом [0] — совпавшая (под)строка целиком, 1-й элемент — первая группа, 2-й — вторая и т.д. — все в соответствии с группами определенными в шаблоне.
Иногда кроме поиска требуется определить положение образца в тексте. Это можно сделать с помощью метода search().
В Питоне этот метод относится к регулярным выражениям и возвращает совпавший объект. У этого совпавшего объекта есть метод start(), возвращающий начало вхождения этой подстроки в основную строку:
text = 'Say hi at hi@example.com' first_match = pattern.search(text) if first_match: start = first_match.start() # start == 10
В JS метод есть строковый метод search() возвращающий индекс начала подстроки. Или -1 если совпадений не было найдено.
text = 'Say hi at hi@example.com'; first_match = text.search(pattern); if (first_match > -1) { start = first_match; // start === 10 }
Замена по шаблону с помощью регулярных выражений
Обычно замена по шаблону нужна когда требуется очистить данные или добавить какие-нибудь свойства в текст. К примеру, мы можем взять строку и все встречающиеся email-адреса сделать ссылками:
В Питоне для этого есть метод шаблона регулярных выражений sub():
html = pattern.sub( r'<a href="mailto:\g<0>">\g<0></a>', 'Say hi at hi@example.com', ) # html == 'Say hi at <a href="mailto:hi@example.com">hi@example.com</a>'
Разработчики на JS могут использовать строковый метод replace():
html = 'Say hi at hi@example.com'.replace( pattern, '<a href="mailto:$&">$&</a>', ); // html === 'Say hi at <a href="mailto:hi@example.com">hi@example.com</a>'
В Питоне совпавшие группы доступны как \g<0>, \g<1>, \g<2> и т.д.
В JS аналогично $&, $1, $2 и т.д.
Также возможно заменить совпавший участок при помощи функции. Такие функции бывают полезны при замене оборачивании исходного текста или для подсчета, сбора или получения другой информации о тексте. К примеру, использую при замене вызов функции можно написать подсветку синтаксиса HTML.
Давайте изменим все встречающиеся e-mail адреса ПРОПИСНЫМИ БУКВАМИ.
В Питоне функция замены получает совпавший объект. Мы используем метод group() чтобы произвести действия с совпавшим текстом и вернуть его в виде замены:
text = pattern.sub( lambda match: match.group(0).upper(), 'Say hi at hi@example.com', ) # text == 'Say hi at HI@EXAMPLE.COM'
В JS функция замены получает совпавшую строку целиком, первое вхождение, второе и т.д. Мы производим необходимые действия и возвращаем измененную строку:
text = 'Say hi at hi@example.com'.replace( pattern, function(match, p1, p2) { return match.toUpperCase(); } ); // text === 'Say hi at HI@EXAMPLE.COM'
Обработка ошибок
В противоположность Питону фронтенд-браузерный JavaScript обычно не используют для записи-чтения файлов или доступа к базам данных. Поэтому блоки try..catch довольно редко встречаются в JS по сравнению с блоками try..except в Питоне.
Но, в любом случае, обработка ошибок может быть произведена в пользовательском скрипте, вызвана из библиотечной функции и перехвачена в основном коде.
В следующем примере на Пионе мы определим свое исключение MyException, порождаем его в функции, и посмотрим как его перехватить и обработать в блоке try..except..finally:
class MyException(Exception): def __init__(self, message): self.message = message def __str__(self): return self.message def proceed(): raise MyException('Error happened!') try: proceed() except MyException as err: print('Sorry! {}'.format(err)) finally: print('Finishing')
Следующий код на JS делает то же самое — мы определяем класс MyException, порождаем его в функции, перехватываем и обрабатываем в блоке try..catch..finally:
function MyException(message) { this.message = message; this.toString = function() { return this.message; } } function proceed() { throw new MyException('Error happened!'); } try { proceed(); } catch (err) { if (err instanceof MyException) { console.log('Sorry! ' + err); } } finally { console.log('Finishing'); }
В обоих языках класс MyException имеет параметр message и метод для строкового представления в зависимости от значения message.
Конечно, исключения должны вызваться/порождаться только в случае ошибки. И если вы определили эту ошибку в своем модуле.
Выводы
- Сериализация в/из JSON достаточно прямолинейна — что в Питоне что в JS.
- Регулярки — мощный инструмент обработки текстов в обоих языках.
- Можно производить замены с помощью функций.
- Для более сложных случаев можно использовать вызов, перехват и обработку ошибок.
Как было сказано в прошлый раз, можно построчно сопоставить примеры кода приведенного здесь, чтобы понять схожие структуры данных и методы работы с ними: строки, массивы, словари, доступ к объектам.
В следующей части поговорим о текстовых шаблонах, распаковке списков, лямбда-функциях, итерациях без использования индексов, генераторах и множествах.
