Приведи примеры изменяемых (mutable) структур данных в Python.

Ответ

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

Основные встроенные изменяемые типы:

  1. Список (list): Упорядоченная коллекция.

    my_list = [1, 2, 3]
    my_list[0] = 100      # Изменение элемента по индексу
    my_list.append(4)     # Добавление в конец
    my_list.extend([5, 6]) # Расширение списка
    my_list.sort()        # Сортировка на месте
    print(my_list)        # [1, 2, 3, 4, 5, 6]? Нет! [100, 2, 3, 4, 5, 6]
  2. Словарь (dict): Коллекция пар ключ-значение.

    my_dict = {'name': 'Alice', 'age': 30}
    my_dict['age'] = 31          # Изменение значения
    my_dict['city'] = 'Berlin'   # Добавление новой пары
    my_dict.update({'job': 'Engineer'})
    value = my_dict.pop('name')  # Удаление с возвратом значения
    print(my_dict)               # {'age': 31, 'city': 'Berlin', 'job': 'Engineer'}
  3. Множество (set): Неупорядоченная коллекция уникальных элементов.

    my_set = {1, 2, 3}
    my_set.add(4)          # Добавление элемента
    my_set.remove(2)       # Удаление элемента (KeyError если нет)
    my_set.discard(10)     # Безопасное удаление (не вызывает ошибки)
    my_set.clear()         # Очистка всего множества
    print(my_set)          # set()

Важные следствия изменяемости:

  • Присваивание по ссылке: При присваивании new_list = my_list создается не копия, а новая ссылка на тот же объект.

    original = [1, 2, 3]
    alias = original
    alias.append(4)
    print(original)  # [1, 2, 3, 4] - изменился и оригинал!

    Решение: использовать копирование (copy() для поверхностной копии, deepcopy() из модуля copy для вложенных структур).

  • Аргументы по умолчанию в функциях: Изменяемый объект как аргумент по умолчанию создается один раз при определении функции.

    def append_to(element, target=[]):  # ОПАСНО: target создается один раз!
        target.append(element)
        return target
    
    print(append_to(1))  # [1]
    print(append_to(2))  # [1, 2] - сюрприз!

    Правильный паттерн:

    def append_to(element, target=None):
        if target is None:
            target = []  # Новый список при каждом вызове без аргумента
        target.append(element)
        return target

К другим изменяемым типам относятся bytearray, array.array, объекты collections (например, deque, Counter), а также экземпляры пользовательских классов (по умолчанию).

Ответ 18+ 🔞

А, ну вот, опять про эти ваши изменяемые объекты. Сидишь такой, пишешь код, всё вроде летит, а потом — бац! — и ты уже сам от себя охуел, потому что список, который ты вроде бы в другую переменную положил, внезапно мутировал, как мартышлюшка на банане. Давай разжую, как есть, без соплей.

Вот смотри. Изменяемые объекты — это такие штуки, которые можно поменять на месте, не создавая новый объект. Как будто ты перекрасил свою тачку — она та же, но цвет другой. С неизменяемыми так не прокатит, там придётся новую тачку покупать. И вот это «на месте» — оно и сила, и главная подстава, ёпта.

Основные бандиты, которые всё портят:

  1. Список (list). Классика жанра, хитрая жопа. Создал — и понеслась.

    my_list = [1, 2, 3]
    my_list[0] = 100      # Щёлк — и первый элемент уже не тот
    my_list.append(4)     # Добавил с хвоста
    my_list.extend([5, 6]) # Впендюрил ещё кучу
    my_list.sort()        # Перетряхнул на месте
    print(my_list)        # Смотри-ка: [100, 2, 3, 4, 5, 6]
  2. Словарь (dict). Тут ключи — как сейфы, а значения — что в них лежит. И лежать там может что угодно, и меняться когда угодно.

    my_dict = {'name': 'Алиса', 'age': 30}
    my_dict['age'] = 31          # Раз — и Алиса постарела на год
    my_dict['city'] = 'Берлин'   # Добавил целый город, ядрёна вошь!
    my_dict.update({'job': 'Инженер'}) # А теперь и профессия есть
    value = my_dict.pop('name')  # Выдернул имя и сохранил
    print(my_dict)               # Глянь: {'age': 31, 'city': 'Берлин', 'job': 'Инженер'}
  3. Множество (set). Мешок с уникальными штуками. Кидаешь туда-сюда, а дубликаты сами исчезают.

    my_set = {1, 2, 3}
    my_set.add(4)          # Кинул четвёрку
    my_set.remove(2)       # Выкинул двойку (если нет — будет скандал, KeyError)
    my_set.discard(10)     # А вот это вежливое удаление — если нет, то и ладно
    my_set.clear()         # Раз — и пустота. Всё вымел.
    print(my_set)          # set()

А теперь, блядь, самое важное — где все обламываются:

  • Присваивание — это не копирование. Это как дать другу ключи от своей квартиры и сказать «поживи». А он там вечеринку устроит.

    original = [1, 2, 3]
    alias = original          # Это не копия! Это второй ключ от той же квартиры!
    alias.append(4)           # Твой друг повесил новую картину.
    print(original)           # [1, 2, 3, 4] — и в твоей-то квартире она тоже висит! Вот же ж пиздец.

    Решение? Копировать, чёрт возьми! .copy() для простого, а если там внутри ещё списки сидят — deepcopy() из модуля copy, чтоб наверняка.

  • Аргументы по умолчанию в функциях — это ловушка для молодых. Тут волнение ебать, потому что список как значение по умолчанию создаётся один раз, когда функцию определяют, а не когда вызывают.

    def append_to(element, target=[]):  # ОПАСНОСТЬ! target родился тут и будет жить вечно.
        target.append(element)
        return target
    
    print(append_to(1))  # [1] — вроде норм.
    print(append_to(2))  # [1, 2] — а вот нихуя себе! Он же помнит прошлый вызов!

    Как делать правильно? Всегда None по умолчанию, а внутри проверяй.

    def append_to(element, target=None):
        if target is None:   # Вот тут доверия ебать ноль к тому, что тебе передали.
            target = []      # Создаём новый, чистенький список каждый раз.
        target.append(element)
        return target

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