Продолжаем публикацию перевода серии статей о схожести и различии двух языков.
Сегодня поговорим о сериализации словарей, 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.
- Регулярки — мощный инструмент обработки текстов в обоих языках.
- Можно производить замены с помощью функций.
- Для более сложных случаев можно использовать вызов, перехват и обработку ошибок.
Как было сказано в прошлый раз, можно построчно сопоставить примеры кода приведенного здесь, чтобы понять схожие структуры данных и методы работы с ними: строки, массивы, словари, доступ к объектам.
В следующей части поговорим о текстовых шаблонах, распаковке списков, лямбда-функциях, итерациях без использования индексов, генераторах и множествах.