← Назад к вопросам

Как сделать все значения списка (list) уникальными?

1.2 Junior🔥 81 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как сделать все значения списка (list) уникальными

Удаление дубликатов из списка — частая задача. Есть несколько подходов с разными характеристиками по производительности и сохранению порядка.

1. Использование set() — простейший подход

numbers = [1, 2, 2, 3, 3, 3, 4]
unique_numbers = list(set(numbers))
print(unique_numbers)  # [1, 2, 3, 4] или другой порядок

Плюсы:

  • Самый быстрый способ O(n)
  • Код простой и понятный

Минусы:

  • НЕ сохраняет порядок элементов
  • Не работает с неhashable типами (списки, словари, множества)

2. Сохранение порядка с dict.fromkeys()

numbers = [1, 2, 2, 3, 3, 3, 4, 5, 1]
unique_numbers = list(dict.fromkeys(numbers))
print(unique_numbers)  # [1, 2, 3, 4, 5]

Как это работает:

  • dict.fromkeys() создаёт словарь с уникальными ключами
  • С Python 3.7+ словари сохраняют порядок вставки
  • list() конвертирует ключи словаря обратно в список

Плюсы:

  • Быстрый O(n)
  • Сохраняет оригинальный порядок
  • Чистый и понятный код

Минусы:

  • Не работает с неhashable типами

3. Циклический подход с проверкой

numbers = [1, 2, 2, 3, 3, 3, 4]
unique_numbers = []

for num in numbers:
    if num not in unique_numbers:
        unique_numbers.append(num)

print(unique_numbers)  # [1, 2, 3, 4]

Плюсы:

  • Сохраняет порядок
  • Работает с любыми типами
  • Понятный код

Минусы:

  • Медленный O(n²) — проверка в списке для каждого элемента
  • Неэффективно для больших списков

4. Использование set для отслеживания

numbers = [1, 2, 2, 3, 3, 3, 4]
seen = set()
unique_numbers = []

for num in numbers:
    if num not in seen:
        seen.add(num)
        unique_numbers.append(num)

print(unique_numbers)  # [1, 2, 3, 4]

Плюсы:

  • Сохраняет порядок
  • Быстрый O(n) — проверка в set
  • Работает с hashable типами

Минусы:

  • Немного больше кода

5. Для неhashable типов (списки, словари)

data = [
    {"id": 1, "name": "Alice"},
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
]

# Способ 1: Конвертировать в tuple для hashable типа
from typing import Any

def make_hashable(obj: Any) -> Any:
    if isinstance(obj, dict):
        return tuple(sorted(obj.items()))
    elif isinstance(obj, list):
        return tuple(make_hashable(item) for item in obj)
    return obj

unique_data = []
seen = set()

for item in data:
    hashable = make_hashable(item)
    if hashable not in seen:
        seen.add(hashable)
        unique_data.append(item)

print(unique_data)
# [
#     {"id": 1, "name": "Alice"},
#     {"id": 2, "name": "Bob"},
# ]

6. Использование library функций

pandas для больших данных:

import pandas as pd

numbers = [1, 2, 2, 3, 3, 3, 4]
unique_numbers = pd.Series(numbers).drop_duplicates().tolist()
print(unique_numbers)  # [1, 2, 3, 4]

Для объектов с ID (например, БД запросы):

users = [
    {"id": 1, "name": "Alice"},
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
]

unique_users = {
    user["id"]: user
    for user in users
}.values()

print(list(unique_users))

7. Сравнение производительности

import time

numbers = list(range(10000)) * 10  # 100000 элементов

# Способ 1: set()
start = time.time()
result = list(set(numbers))
print(f"set(): {time.time() - start:.4f}s")

# Способ 2: dict.fromkeys()
start = time.time()
result = list(dict.fromkeys(numbers))
print(f"dict.fromkeys(): {time.time() - start:.4f}s")

# Способ 3: Цикл с set
start = time.time()
seen = set()
result = []
for num in numbers:
    if num not in seen:
        seen.add(num)
        result.append(num)
print(f"цикл + set: {time.time() - start:.4f}s")

# Способ 4: Цикл с проверкой в списке
start = time.time()
result = []
for num in numbers:
    if num not in result:
        result.append(num)
print(f"цикл в списке: {time.time() - start:.4f}s")  # Медленный!

Результаты (примерно):

set(): 0.0001s          ✓ Быстро
dict.fromkeys(): 0.0005s ✓ Быстро, сохраняет порядок
цикл + set: 0.0008s     ✓ Быстро
цикл в списке: 2.5000s  ✗ МЕДЛЕННО!

8. Практические примеры

Удаление дубликатов из тегов:

def normalize_tags(tags: list[str]) -> list[str]:
    """Удаляет дубликаты и нормализует теги"""
    # Преобразуем, убираем пробелы, приводим к нижнему регистру
    normalized = [tag.strip().lower() for tag in tags]
    # Удаляем дубликаты, сохраняя порядок
    return list(dict.fromkeys(normalized))

tags = ["Python", "python", "Django", "Python", "Flask"]
print(normalize_tags(tags))  # ["python", "django", "flask"]

Удаление дубликатов в потоке данных (generator):

def unique_iterator(iterable):
    """Генератор для удаления дубликатов из потока"""
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

# Использование
data = [1, 2, 2, 3, 3, 3]
for unique in unique_iterator(data):
    print(unique)  # 1, затем 2, затем 3

Для объектов по определённому полю:

class User:
    def __init__(self, id, name):
        self.id = id
        self.name = name

users = [
    User(1, "Alice"),
    User(1, "Alice"),
    User(2, "Bob"),
]

# Удаляем дубликаты по ID
unique_users = {user.id: user for user in users}.values()
print(list(unique_users))  # 2 пользователя

Рекомендации по выбору

СитуацияМетодПримечание
Просто удалить дубликатыlist(set(list))Если порядок не важен
Сохранить порядокlist(dict.fromkeys(list))Обычное решение
Большие данныеpandas или numpyДля оптимизации памяти
Неhashable типыЦикл + set с hashable ключамиИли dict.fromkeys с ключом
Потоковая обработкаGenerator с setДля больших данных

Лучшие практики

  • По умолчанию используй dict.fromkeys() — быстро, сохраняет порядок, понятно
  • Если порядок не важен, используй set() — на микросекунду быстрее
  • Избегай циклических проверок в списке — O(n²) очень медленно
  • Для больших данных профилируй — используй timeit для сравнения
  • Документируй намерение — почему удаляешь дубликаты и какой порядок нужен
Как сделать все значения списка (list) уникальными? | PrepBro