Работа со строками в Python. Готовимся к собеседованию: вспоминаем азы

Обложка поста
Петр Смолович

Петр Смолович, ведущий разработчик хостинг-провайдера и регистратора доменов REG.RU

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

Итак, мы на собеседовании, и я хочу узнать, умеете ли вы обращаться со строками.

Как склеить две строки?

>>> s = "abc" + "def"
>>> s += "xyz"

Элементарно? Почти. Важно помнить, что строки — это неизменяемые объекты. Каждый раз, когда мы говорим про «изменение» строки, технически мы создаем новый объект и записываем туда вычисленное значение.

А как склеить три строки? Напрашивается ответ «точно так же», и иногда это самый лучший способ. Но интервьюер скорее всего хочет проверить, знаете ли вы про метод .join().

>>> names = ["John", "Paul", "Ringo", "George"]
>>> ", ".join(names)
'John, Paul, Ringo, George'

join() — очень удобный метод, позволяющий склеить N строк, причём с произвольным разделителем.

Здесь важно не только получить результат, но и понимать, как работает приведённая конструкция. А именно, что join() — это метод объекта «строка», принимающий в качестве аргумента список и возвращающий на выходе новую строку.
Кстати, хорошая задачка для интервью — написать свою реализацию join().

Разделить строки?

Есть несколько способов получить часть строки. Первый — это split, обратный метод для join. В отличие от join’а, он применяется к целевой строке, а разделитель передаётся аргументом.

>>> s = "Альфа, Браво, Чарли"
>>> s.split(", ")
['Альфа', 'Браво', 'Чарли']

Второй — срезы (slices).

Срез s[x:y] позволяет получить подстроку с символа x до символа y. Можно не указывать любое из значений, чтобы двигаться с начала или до конца строки. Отрицательные значения используются для отсчёта с конца (-1 — последний символ, -2 — предпоследний и т.п.).

>>> s = "Hello, world!"
>>> print(s[0:5])
Hello
>>> print(s[-6:])
world!

При помощи необязательного третьего параметра s[x:y:N] можно выбрать из подстроки каждый N-ый символ. Например, получить только чётные или только нечётные символы:

>>> s = "0123456789"
>>> print(s[::2])
02468
>>> print(s[1::2])
13579

Что насчёт поиска в строке?

Самое быстрое — проверить, начинается ли (заканчивается ли) строка с выбранных символов. Для этого в Python предусмотрены специальные строковые методы.

>>> s = "0123456789"
>>> s.startswith("012")
True
>>> s.endswith("69")
False

Для поиск подстроки в произвольном месте есть метод с говорящим названием find(). Он вернет индекс начала найденного вхождения подстроки в строку, либо -1, если ничего не найдено.

>>> s = "0123456789"
>>> s.find("45")
4
>>> s.find("42")
-1

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

>>> import re
>>> s = "https://www.reg.ru/hosting/";
>>> result = re.match(r"^(http|https)://([^/]+)(.*)$", s)
>>> print(result.group(1))
https
>>> print(result.group(2))
www.reg.ru
>>> print(result.group(3))
/hosting/

А замену в строке сделать сможете?

Во-первых, при помощи срезов и склейки строк можно заменить что угодно.

>>> s = "Hello, darling! How are you?"
>>> s[:7] + "Василий" + s[14:]
'Hello, Василий! How are you?'

Во-вторых, умеешь find(), умей и replace().

>>> s.replace("darling", "Василий")
'Hello, Василий! How are you?'

В-третьих, любую проблему можно решить регулярными выражениями. Либо получить две проблемы 🙂 В случае с заменой вам нужен метод re.sub().

>>> s = "https://www.reg.ru/hosting/";
>>> import re
>>> print(re.sub('[a-z]', 'X', s))
XXXXX://XXX.XXX.XX/XXXXXXX/

Посимвольная обработка?

Есть бесчисленное множество задачек, которые можно решить, пройдясь в цикле по строке. Например, посчитать количество букв «о».

>>> s = "Hello, world!"
>>> for c in s:
>>>     if c == "o":
>>>         counter += 1
>>> print(counter)
2

Иногда удобнее бежать по индексу.

>>> for i in range(len(s)):
>>>     if s[i] == "o":
>>>         counter += 1

Помним, что строки неизменяемы, поэтому подменить i-ый символ по индексу не получится, нужно создавать новый объект:

>>> s[i] = "X"
Traceback (most recent call last):
File "", line 1, in 
TypeError: 'str' object does not support item assignment
>>> s[:i] + "X" + s[i+1:]
'HellX, world!'

Либо можно преобразовать строку в список, сделать обработку, а потом склеить список обратно в строку:

>>> arr = list(s)
>>> "".join(arr)
'Hello, world!'

А форматирование строк?

Типичная жизненная необходимость — сформировать строку, подставив в неё результат работы программы. Начиная с Python 3.6, это можно делать при помощи f-строк:

>>> f"Строка '{s}' содержит {len(s)} символов."
"Строка 'Hello, world!' содержит 13 символов."

В более старом коде можно встретить альтернативные способы

>>> "Строка '%s' содержит %d символов" % (s, len(s))
>>> "Строка '{}' содержит {} символов".format(s, len(s))

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

>>> "Строка '" + s + "' содержит " + str(len(s)) + " символов."
"Строка 'Hello, world!' содержит 13 символов."

***

Цель работодателя на собеседовании — убедиться что вы соображаете и что вы справитесь с реальными задачами. Однако погружение в реальные задачи занимает несколько недель, а время интервью ограничено. Поэтому вас ждут учебные задания, которые можно решить за 10-30 минут, а также вопросы на понимание того, как работает код. О них и поговорим в следующей части.