Генерация строк в циклах — задача, часто возникающая на практике. Типичный пример — функция, помогающая генерировать HTML-списки. Она принимает на вход коллекцию элементов и возвращает HTML-список из них:
coll = ["milk", "butter"]
build_HTML_list(coll)
# <ul><li>milk</li><li>butter</li></ul>
Как можно решить эту задачу в лоб:
- Создать переменную resultи записать в нее<ul>.
- Пройтись циклом по элементам коллекции и дописать в результирующую строку очередной элемент <li>.
- Добавить в конце </ul>и вернутьresultиз функции.
def build_HTML_list(coll):
    result = "<ul>"
    for item in coll:
        result = f"{result}<li>{item}</li>"
        # либо так: result += '<li>{item}</li>'
    result = f"{result}</ul>"
    return result
coll = ["milk", "butter"]
print(build_HTML_list(coll))
# => <ul><li>milk</li><li>butter</li></ul>
Такой способ вполне рабочий, но для большинства языков программирования максимально неэффективный. Дело в том, что конкатенация и интерполяция порождают новую строчку вместо старой. Подобная ситуация повторяется на каждой итерации. Причем строка становится все больше и больше. Копирование строк приводит к серьезному расходу памяти и может влиять на производительность. Конечно, для большинства приложений данная проблема неактуальна из-за малого объема прогоняемых данных, но более эффективный подход не сложнее в реализации и обладает рядом плюсов. Поэтому стоит сразу приучить себя работать правильно.
Правильно, в случае с динамическими языками – формировать список, который затем с помощью метода join() можно превратить в строку:
def build_HTML_list(coll):
    parts = []
    for item in coll:
        parts.append(f"<li>{item}</li>")
    # Метод join объединяет элементы списка в строку
    # В качестве разделителя между значениями
    # используется значение строки
    inner_value = "".join(parts)
    result = f"<ul>{inner_value}</ul>"
    return result
Размер кода практически не изменился, но способ формирования результата стал другим. Вместо строки, сначала собирается список, который затем превращается в строку с помощью метода .join(). У такого подхода есть и дополнительные плюсы:
- Такой код проще отлаживать. Данные, представленные списком, легче вычленять визуально и программно.
- Список — это структура, с ним можно производить дополнительные манипуляции. С готовой строкой уже ничего особо не сделать.
Регулируя разделитель, строки можно объединять разными способами. Например, через запятую с пробелом:
parts = ["python", "PHP", "Python"]
output = ", ".join(parts)
print(output)  # => python, PHP, Python
Если каждое слово надо вывести на новой строчке, то в качестве разделителя используем символ перевода строки '\n':
parts = ["python", "PHP", "Python"]
# Теперь каждое слово будет начинаться с новой строки
output = "\n".join(parts)
print(output)
# => python
# => PHP
# => Python
Последний пример особенно важен. Новички часто допускают ошибку и добавляют перевод строки в момент формирования списка, а не в join(). Посмотрите на пример с нашей функцией build_HTML_list().
Правильно:
def build_HTML_list(coll):
    parts = []
    for item in coll:
        parts.append(f"<li>{item}</li>")
    inner_value = "\n".join(parts)  # перевод строки
    result = f"<ul>{inner_value}</ul>"
    return result
coll = ["milk", "butter"]
build_HTML_list(coll)
print  # => <ul><li>milk</li>
print  # => <li>butter</li></ul>
Неправильно:
def build_HTML_list(coll):
    parts = []
    for item in coll:
        parts.append(f"\n<li>{item}</li>")
    inner_value = "".join(parts)  # разделителя нет
    result = f"<ul>{inner_value}</ul>"
    return result
coll = ["milk", "butter"]
build_HTML_list(coll)
print  # => <ul>
print  # => <li>milk</li>
print  # => <li>butter</li></ul>
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.