1na1
@1na1
Hello, world!

Почему python возвращает существующий объект, вместо создания нового?

Столкнулся с интересным поведением python при созднии небольших встроенных объектов. При попытке создания, например, трех пустых объектов set python вернет один объект трижды. Я наивно считал, что скобки в конструкции SomeClass() в python - это аналог оператора new в других языках

У меня не получается сформулировать запрос к гуглу, поэтому мой вопрос: как гарантированно создавать новый объект при каждом вызове set() и какова вообще логика создания встроенных объектов в python?

Буду рад ссылке на статью, объяснение и (особенно) на документацию

python 3.11

Минималистичный пример - пытаюсь создать три объекта set, а получается только один

for _ in range(3):
    print(id(set()))  # Ождидаю, что set() создаст уникальный объект при каждом вызове

""" Вывод
 2511997244736
 2511997244736
 2511997244736
"""

# id одинаковые, это один объект, а не три


А вот как это может сыграть злую шутку

# Создаю словарь, где значением является set

set_dict: dict[int, set] = dict.fromkeys(range(3), set())  # Ожидаю, что set() создаст уникальный объект для каждого ключа словаря

set_dict[1].add("some_value")  # Пытаюсь добавить строку в один set по конкретному адресу set_dict[1]

print(set_dict)  # А добавляется сразу в "три"

""" Вывод
 {0: {'some_value'}, 1: {'some_value'}, 2: {'some_value'}}
"""
  • Вопрос задан
  • 156 просмотров
Решения вопроса 1
В вашем примере выражение set() вычисляется один раз, а уже затем вызывается fromkeys. Это не имеет никакого отношения к set, так работают функции почти во всех языках программирования - сначала вычисляются значения аргументов, а потом с этими значениями вызывается функция. Итого, у вас везде передаётся один и тот же объект set. Будь там не set, а какой-нибудь Student, было бы то же самое.
Правильный вариант: set_dict = { x: set() for x in range(3) }
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
Maksim_64
@Maksim_64
Data Analyst
Надо же, выглядит все это крайне сомнительно. Сеты мутабельные объекты и оптимизация, как c int или str тут не при чем. При том такое поведение только если я создаю объект налету. К примеру если я соберу их в список
empty_sets = []
for _ in range(3):
    empty_sets.append(set())

for obj in empty_sets:
    print(id(obj))

То работает как и ожидается.
Или
a = set()
b = set()
a is b
Тоже как и ожидается.

Пока экспериментировал вот что обнаружил
for _ in range(3):
    print(id([]))

Тоже самое при этом
for _ in range(3):
    print(id(list()))

Как и ожидается объекты разные.

Мое мнение крайне сомнительное поведение.
Ответ написан
Комментировать
Vindicar
@Vindicar
RTFM!
А разгадка проста - сборщик мусора.
Созданный тобой set() уничтожается сразу же после выхода из функции id(), так как на него не остаётся ссылок.
Следующий set() создаётся по свежеосвобождённому адресу, а потому имеет тот же самый id().

Ну и да, set() не является hashable(), а потому не может быть ключом в словаре.

sets = [set() for _ in range(3)]
for s in sets:
    print(id(s))

Этот код выведет разные id(), потому что ссылки на объекты остаются в списке sets(), а значит, новые множества создаются не там же, где старые.
Ответ написан
Комментировать
@Everything_is_bad
а теперь пробуй такой, скорее всего как всегда оптимизация из-за несвязывания с реальным объектом
for _ in range(3):
    x = set()
    print(id(x))
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы