Дан фрагмент Python: def unique_items(lst): return list(set(lst)) — объясните, почему такой код может нарушать порядок элементов, напишите юнит‑тесты, демонстрирующие проблему, и предложите исправленную реализацию
Коротко — почему: `set` не сохраняет порядок элементов; `list(set(lst))` удаляет дубликаты, но итерирует элементы в произвольном (зависящем от хешей и реализации) порядке, поэтому исходный порядок нарушается. Юнит‑тесты (unittest), которые проверяют сохранение первого вхождения и продемонстрируют проблему при использовании `list(set(...))`: ```python import unittest # исходная проблемная реализация def unique_items_bad(lst): return list(set(lst)) class TestUniqueItems(unittest.TestCase): def test_preserve_order_strings(self): lst = ['a', 'b', 'a', 'c', 'b'] # ожидаем первый порядок появления: 'a','b','c' self.assertEqual(unique_items_bad(lst), ['a', 'b', 'c']) def test_preserve_order_numbers(self): lst = [3, 1, 2, 3, 2, 1] self.assertEqual(unique_items_bad(lst), [3, 1, 2]) if __name__ == '__main__': unittest.main() ``` Эти тесты проверяют семантику «оставить только первые вхождения в исходном порядке». При запуске с проблемной реализацией тесты, как правило, падают (потому что `unique_items_bad` может вернуть элементы в другом порядке). Правильные реализации (рекомендуемые): 1) Простая и быстрая (Python 3.7+, порядок словарей гарантирован по спецификации): ```python def unique_items(lst): return list(dict.fromkeys(lst)) ``` 2) Надёжная реализация, которая также корректно работает с не‑хешируемыми элементами (например, списками): ```python def unique_items(lst): seen = set() result = [] for x in lst: try: if x not in seen: seen.add(x) result.append(x) except TypeError: # x не-хешируемый — сравниваем полноценно по равенству среди уже добавленных if x not in result: result.append(x) return result ``` Пояснения: - `dict.fromkeys` сохраняет порядок первых ключей и кратко решает задачу для хешируемых объектов. - Вторая реализация даёт корректное поведение и когда элементы не‑хешируемы (добавляет в результат только при отсутствии равного элемента).
Юнит‑тесты (unittest), которые проверяют сохранение первого вхождения и продемонстрируют проблему при использовании `list(set(...))`:
```python
import unittest
# исходная проблемная реализация
def unique_items_bad(lst):
return list(set(lst))
class TestUniqueItems(unittest.TestCase):
def test_preserve_order_strings(self):
lst = ['a', 'b', 'a', 'c', 'b']
# ожидаем первый порядок появления: 'a','b','c'
self.assertEqual(unique_items_bad(lst), ['a', 'b', 'c'])
def test_preserve_order_numbers(self):
lst = [3, 1, 2, 3, 2, 1]
self.assertEqual(unique_items_bad(lst), [3, 1, 2])
if __name__ == '__main__':
unittest.main()
```
Эти тесты проверяют семантику «оставить только первые вхождения в исходном порядке». При запуске с проблемной реализацией тесты, как правило, падают (потому что `unique_items_bad` может вернуть элементы в другом порядке).
Правильные реализации (рекомендуемые):
1) Простая и быстрая (Python 3.7+, порядок словарей гарантирован по спецификации):
```python
def unique_items(lst):
return list(dict.fromkeys(lst))
```
2) Надёжная реализация, которая также корректно работает с не‑хешируемыми элементами (например, списками):
```python
def unique_items(lst):
seen = set()
result = []
for x in lst:
try:
if x not in seen:
seen.add(x)
result.append(x)
except TypeError:
# x не-хешируемый — сравниваем полноценно по равенству среди уже добавленных
if x not in result:
result.append(x)
return result
```
Пояснения:
- `dict.fromkeys` сохраняет порядок первых ключей и кратко решает задачу для хешируемых объектов.
- Вторая реализация даёт корректное поведение и когда элементы не‑хешируемы (добавляет в результат только при отсутствии равного элемента).