Код
Python
#База знаний

Как объединить строки в Python: использование .join() вместо +

Понятное — не всегда значит лучшее.

: официальный арт к игре «ведьмак 3» / cd projekt red

Python ценят за его интуитивную понятность. Так, строки можно объединить простым плюсом:

s1 = 'Я люблю '
s2 = 'программировать на Python'
s1 + s2
>>> 'Я люблю программировать на Python'

На первый взгляд странно, что многие профессиональные Python-разработчики не пользуются этим очевидным и, казалось бы, удобным методом, а вместо него применяют .join().

Почему — давайте разбираться.

Что ещё за .join() такой?

Возьмём те же строки s1 и s2 и попробуем повторить предыдущий результат с помощью .join():

''.join([s1, s2])
>>> 'Я люблю программировать на Python' 

Первая мысль: что здесь происходит? Зачем пустая строка? Почему строки s1 и s2 засунуты в список? Почему всё так не наглядно? А это точно Python?

Итак, по порядку:

  • Пустая строка ' ' — это строка-соединитель: то, что будет между строками s1 и s2. В нашем случае она пустая, но это не обязательно — там может быть, например, пробел или знак препинания.
  • Строки s1 и s2 составлены в список [s1, s2] потому, что join() принимает только один аргумент. Как правило, это итерируемый объект — список, кортеж или словарь из строк. В случае словаря функция join() объединит его ключи.
''.join((s1, s2))  # кортеж
>>> 'Я люблю программировать на Python'

''.join({s1:'a', s2:'b'})  # словарь с s1 и s2 в качестве ключей
>>> 'Я люблю программировать на Python'

''.join({'a':s1, 'b':s2})  # словарь с s1, s2 в качестве значений
>>> 'ab'

Вроде бы с join() разобрались. Но главный вопрос остался открытым: «Зачем его использовать?»

Когда строк много

Представим, что вы получили много строк в одном списке и теперь надо объединить их в одну. Попробуем решить задачу с помощью плюса:

strings = ['Жизнь', 'слишком', 'коротка,', 'программируй', 'на', 'Python']

def join_strs(strs):
    result = ''
    for s in strs:
         result += ' ' + s  # а вот и плюс
    return result[1:]

join_strs(strings)
>>> 'Жизнь слишком коротка, программируй на Python'

В теле функции join_strs() мы создаём пустую строку result. Далее с помощью цикла for бежим по полученному списку из строк и с каждой итерацией добавляем к строке result пробел и строку из списка — с помощью плюса, разумеется.

В конце return возвращает строку result без первого элемента — там стоит ненужный нам пробел, добавившийся при первой итерации.

Итак, чтобы использовать ‘+’ для объединения строк из списка:

  • нужен цикл for,
  • нужно помнить о лишнем пробеле.

Посмотрим, что может предложить join():

def join_strs_better(strs):
    return ' '.join(strs)

join_strs_better(strings)
>>> 'Жизнь слишком коротка, программируй на Python'

Ого! Выглядит неплохо: никаких циклов и не надо беспокоиться о пробеле — функция сама обо всём позаботилась, причём в одну строчку.

В принципе, этого уже достаточно, чтобы забыть о ‘+’ — во всяком случае, для большого количества строк. Но есть ещё одна — возможно, главная — причина.

Производительность

Встроенная Python-библиотека timeit предназначена для измерения времени исполнения кода небольшого размера (скриптов, или сниппетов). Как раз наш случай.

Передадим код из примеров выше в качестве аргумента в функцию timeit.timeit(), исполним его миллион раз для обеих наших функций и сравним время:

import timeit

timeit.timeit(
    '''
strings = ['Жизнь', 'слишком', 'коротка,', 'программируй', 'на', 'Python']
def join_strs(strs):
	result = ''
	for s in strs:
    	result += ' ' + s
	return result[1:]
join_strs(strings)
'''
    , number=1000000)
>>> 1.1924651000001631

timeit.timeit(
    '''
strings = ['Жизнь', 'слишком', 'коротка,', 'программируй', 'на', 'Python']
def join_strs_better(strs):
    return ' '.join(strs)
join_strs_better(strings)
'''
    , number=1000000)
>>> 0.39118409999991854

Объединение шести строк в одну с помощью плюса в три раза медленнее, чем объединение этих же строк с помощью функции join().

С увеличением количества объединяемых строк эта разница будет расти — попробуйте проверить это самостоятельно.

В чём причина?

Как работают + и join()

Приблизительная схема работы цикла for с плюсом внутри выглядит примерно так.

Изображение: Christopher Tao
  1. На каждой итерации цикла из списка вынимается строка.
  2. Интерпретатор выполняет команду result += ' ' + s, сначала запрашивая память для пробела ' '.
  3. Затем он запрашивает память для строки s.
  4. Так происходит шесть раз, и в каждой итерации есть два запроса в память — для пробела и для строки, итого 12.

А что происходит, когда мы объединяем эти же строки с помощью .join()?

Изображение: Christopher Tao
  1. Интерпретатор подсчитывает количество строк в списке: 6.
  2. Подсчитывает количество нужных для объединения пробелов: 5.
  3. Обращается в память сразу за одиннадцатью ячейками.
  4. Формирует строку, выдаёт результат.

Как видим, всё происходит гораздо компактнее. Меньшее количество обращений к памяти — основная причина более высокой скорости работы функции join() по сравнению с циклом for и +.

Подытожим

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

На курсах «Профессии Python-разработчик» вы не только познакомитесь именно с такими инструментами, но и выработаете привычку к постоянному их поиску и совершенствованию. В этом, возможно, и заключается суть программирования. Приходите, и да пребудут с вами Дзен и Дух Пайтона!


Изучайте IT на практике — бесплатно

Курсы за 2990 0 р.

Я не знаю, с чего начать
Научитесь: Профессия Python-разработчик Узнать больше
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована