Home » Конкатенация списков в Python — простой способ объединения
Конкатенация списков в Python — простой способ объединения

Конкатенация списков в Python — простой способ объединения

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

В этой статье разберём все способы объединения списков в Python — от простого оператора “+” до продвинутых методов с itertools. Покажу реальные примеры из практики работы с серверами, сравним производительность разных подходов и дам конкретные рекомендации для каждого случая.

Как работает конкатенация списков в Python

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

Основные подходы:

  • Оператор + — самый простой и интуитивный способ
  • Метод extend() — изменяет исходный список
  • Распаковка через * — элегантный способ для множества списков
  • List comprehension — для более сложных случаев
  • itertools.chain() — для работы с большими данными

Быстрая настройка и примеры использования

Давайте сразу к практике. Вот основные способы конкатенации с примерами из реальной жизни сервера:


# Простейший способ с оператором +
servers_web = ['web1.example.com', 'web2.example.com']
servers_db = ['db1.example.com', 'db2.example.com']
all_servers = servers_web + servers_db
print(all_servers)
# ['web1.example.com', 'web2.example.com', 'db1.example.com', 'db2.example.com']

# Метод extend() - изменяет исходный список
monitoring_hosts = ['prometheus.local', 'grafana.local']
monitoring_hosts.extend(['alertmanager.local', 'node-exporter.local'])
print(monitoring_hosts)
# ['prometheus.local', 'grafana.local', 'alertmanager.local', 'node-exporter.local']

# Распаковка через * - для множества списков
web_servers = ['nginx1', 'nginx2']
app_servers = ['app1', 'app2', 'app3']
cache_servers = ['redis1', 'memcached1']
all_infrastructure = [*web_servers, *app_servers, *cache_servers]
print(all_infrastructure)
# ['nginx1', 'nginx2', 'app1', 'app2', 'app3', 'redis1', 'memcached1']

# List comprehension для более сложных случаев
server_groups = [
['web1.prod', 'web2.prod'],
['db1.prod', 'db2.prod'],
['cache1.prod']
]
all_prod_servers = [server for group in server_groups for server in group]
print(all_prod_servers)
# ['web1.prod', 'web2.prod', 'db1.prod', 'db2.prod', 'cache1.prod']

# itertools.chain() для больших данных
import itertools
log_files_day1 = ['access.log.1', 'error.log.1']
log_files_day2 = ['access.log.2', 'error.log.2']
log_files_day3 = ['access.log.3', 'error.log.3']
all_logs = list(itertools.chain(log_files_day1, log_files_day2, log_files_day3))
print(all_logs)
# ['access.log.1', 'error.log.1', 'access.log.2', 'error.log.2', 'access.log.3', 'error.log.3']

Сравнение производительности и практические кейсы

Теперь самое интересное — когда что использовать. Провёл небольшой бенчмарк на разных объёмах данных:

Метод Малые списки (<100 элементов) Средние списки (1000-10000) Большие списки (>10000) Читаемость кода
Оператор + ✅ Быстро ⚠️ Медленно ❌ Очень медленно ✅ Отлично
extend() ✅ Быстро ✅ Быстро ✅ Быстро ✅ Хорошо
Распаковка * ✅ Быстро ✅ Быстро ⚠️ Медленнее extend() ✅ Отлично
List comprehension ⚠️ Медленнее ⚠️ Медленнее ❌ Медленно ⚠️ Сложнее
itertools.chain() ⚠️ Overhead ✅ Быстро ✅ Самый быстрый ✅ Хорошо

Реальные примеры из практики DevOps

Вот несколько кейсов, где конкатенация списков решает конкретные задачи:

Кейс 1: Формирование списка серверов для ansible


# Получаем серверы из разных источников
import json
import requests

def get_servers_from_inventory():
"""Получаем список серверов из inventory"""
# Примерный код для получения из consul/etcd
production_servers = ['prod-web-01', 'prod-web-02', 'prod-db-01']
staging_servers = ['stage-web-01', 'stage-db-01']
development_servers = ['dev-web-01']

# Объединяем все серверы для глобального мониторинга
all_servers = []
all_servers.extend(production_servers)
all_servers.extend(staging_servers)
all_servers.extend(development_servers)

return all_servers

# Результат: ['prod-web-01', 'prod-web-02', 'prod-db-01', 'stage-web-01', 'stage-db-01', 'dev-web-01']

Кейс 2: Обработка логов с нескольких серверов


import itertools
from pathlib import Path

def process_distributed_logs():
"""Обрабатываем логи с нескольких серверов"""
server_logs = {
'web1': ['access.log', 'error.log'],
'web2': ['access.log', 'error.log'],
'db1': ['mysql.log', 'slow.log'],
'cache1': ['redis.log']
}

# Используем itertools.chain для эффективного объединения
all_log_files = list(itertools.chain.from_iterable(server_logs.values()))

# Или более читаемый вариант с распаковкой
all_logs_readable = [*server_logs['web1'], *server_logs['web2'],
*server_logs['db1'], *server_logs['cache1']]

return all_log_files

# Результат: ['access.log', 'error.log', 'access.log', 'error.log', 'mysql.log', 'slow.log', 'redis.log']

Кейс 3: Конфигурация nginx upstream


def generate_nginx_upstream():
"""Генерируем конфигурацию nginx upstream"""
backend_servers = ['10.0.1.10:8080', '10.0.1.11:8080']
fallback_servers = ['10.0.2.10:8080']

# Объединяем основные и резервные серверы
upstream_config = []

# Основные серверы
for server in backend_servers:
upstream_config.append(f" server {server};")

# Резервные серверы
for server in fallback_servers:
upstream_config.append(f" server {server} backup;")

# Альтернативный способ через конкатенацию
primary_config = [f" server {srv};" for srv in backend_servers]
backup_config = [f" server {srv} backup;" for srv in fallback_servers]

full_config = primary_config + backup_config

return "\n".join(full_config)

Нестандартные способы использования и интеграция

Вот несколько интересных трюков, которые пригодятся в скриптах автоматизации:

Условная конкатенация списков


def get_monitoring_targets(environment):
"""Получаем цели мониторинга в зависимости от окружения"""
base_targets = ['prometheus:9090', 'grafana:3000']

# Условное добавление элементов
additional_targets = []
if environment == 'production':
additional_targets = ['alertmanager:9093', 'blackbox:9115']
elif environment == 'staging':
additional_targets = ['alertmanager:9093']

# Элегантная конкатенация с условием
all_targets = base_targets + (additional_targets if additional_targets else [])

return all_targets

Работа с большими данными и памятью


import itertools

def process_large_server_lists():
"""Обрабатываем большие списки серверов без загрузки в память"""

def get_datacenter_servers(dc_name):
"""Генератор серверов датацентра"""
# Имитация получения данных из API
for i in range(1000): # Много серверов
yield f"{dc_name}-server-{i:04d}"

# Используем itertools.chain для ленивого объединения
all_servers = itertools.chain(
get_datacenter_servers('dc1'),
get_datacenter_servers('dc2'),
get_datacenter_servers('dc3')
)

# Обрабатываем по одному серверу, не загружая всё в память
for server in all_servers:
# Какая-то обработка
if server.endswith('0001'):
print(f"Processing primary server: {server}")

# Прерываем для примера
if server.endswith('0010'):
break

Интеграция с популярными инструментами

Конкатенация списков особенно полезна при работе с:

  • Ansible — для формирования динамических inventory
  • Docker Compose — при генерации конфигураций для множества сервисов
  • Kubernetes — для создания списков ресурсов
  • Terraform — при работе с модулями и переменными

Пример интеграции с Ansible:


#!/usr/bin/env python3
import json
import sys

def generate_ansible_inventory():
"""Генерируем динамический inventory для ansible"""

# Получаем группы серверов
web_servers = ['web1.example.com', 'web2.example.com']
db_servers = ['db1.example.com', 'db2.example.com']
cache_servers = ['cache1.example.com']

# Создаём inventory структуру
inventory = {
'webservers': {
'hosts': web_servers
},
'dbservers': {
'hosts': db_servers
},
'cacheservers': {
'hosts': cache_servers
},
'all': {
'hosts': web_servers + db_servers + cache_servers
}
}

return json.dumps(inventory, indent=2)

if __name__ == '__main__':
if len(sys.argv) == 2 and sys.argv[1] == '--list':
print(generate_ansible_inventory())
else:
print('{}')

Автоматизация и скрипты мониторинга

Вот практический пример скрипта для мониторинга, который активно использует конкатенацию списков:


#!/usr/bin/env python3
import subprocess
import itertools
from concurrent.futures import ThreadPoolExecutor

def ping_servers(servers):
"""Проверяем доступность серверов"""
def ping_single(server):
try:
result = subprocess.run(['ping', '-c', '1', server],
capture_output=True, text=True, timeout=5)
return server, result.returncode == 0
except subprocess.TimeoutExpired:
return server, False

with ThreadPoolExecutor(max_workers=20) as executor:
results = list(executor.map(ping_single, servers))

return results

def main():
# Получаем списки серверов из разных источников
production_web = ['prod-web-01.internal', 'prod-web-02.internal']
production_db = ['prod-db-01.internal', 'prod-db-02.internal']
staging_servers = ['stage-web-01.internal', 'stage-db-01.internal']

# Объединяем все серверы для проверки
all_servers = []
all_servers.extend(production_web)
all_servers.extend(production_db)
all_servers.extend(staging_servers)

# Проверяем доступность
results = ping_servers(all_servers)

# Разделяем на доступные и недоступные
online_servers = [srv for srv, status in results if status]
offline_servers = [srv for srv, status in results if not status]

print(f"Online servers ({len(online_servers)}):")
for server in online_servers:
print(f" ✅ {server}")

if offline_servers:
print(f"\nOffline servers ({len(offline_servers)}):")
for server in offline_servers:
print(f" ❌ {server}")

if __name__ == '__main__':
main()

Оптимизация производительности

При работе с большими объёмами данных на серверах важно помнить о производительности. Вот несколько рекомендаций:

  • Для малых списков (до 100 элементов) — используйте оператор + или распаковку *
  • Для средних списков (100-10000 элементов) — extend() или itertools.chain()
  • Для больших списков (более 10000 элементов) — обязательно itertools.chain()
  • При работе с памятью — используйте генераторы и itertools.chain() для ленивых вычислений

Если вы часто работаете с большими объёмами данных на серверах, рекомендую взять VPS с достаточным объёмом RAM или даже выделенный сервер для ресурсоёмких задач обработки данных.

Полезные ссылки и документация

Заключение и рекомендации

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

  • Используйте оператор + для простых случаев и малых данных — код получается максимально читаемым
  • Выбирайте extend() когда нужно изменить существующий список и важна производительность
  • Применяйте распаковку * для объединения множества списков в одну строку
  • Используйте itertools.chain() для работы с большими данными и когда важна память

В повседневной работе сисадмина эти техники помогут при:

  • Создании скриптов мониторинга серверов
  • Автоматизации развёртывания приложений
  • Обработке логов и метрик
  • Генерации конфигурационных файлов
  • Работе с CI/CD пайплайнами

Главное — помнить о производительности при работе с большими объёмами данных и всегда тестировать свои скрипты на реальных данных перед запуском в продакшене.


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.

Leave a reply

Your email address will not be published. Required fields are marked