Я озадачен таким поведением выделения памяти set s:
>>> set(range(1000)).__sizeof__()
32968
>>> set(range(1000)).union(range(1000)).__sizeof__() # expected, set doesn't change
32968
>>> set(range(1000)).union(list(range(1000))).__sizeof__() #expected, set doesn't change
32968
>>> set(range(1000)).union(set(range(1000))).__sizeof__() # not expected
65736
Почему использование аргумента set в качестве аргумента удваивает количество памяти, используемое в результате set?
Результат в обоих случаях идентичен оригиналу set:
>>> set(range(1000)) == set(range(1000)).union(range(1000)) == set(range(1000)).union(set(range(1000)))
True
Обратите внимание, что то же самое происходит с использованием обычного итератора:
>>> set(range(1000)).union(iter(list(range(1000)))).__sizeof__()
32968
И с помощью метода update:
>>> a.update(range(1000))
>>> a.__sizeof__()
32968
>>> a.update(set(range(1000)))
>>> a.__sizeof__()
65736
Сначала я подумал, что это происходит потому, что когда вызывается union, он видит, что размер другого set равен 1000 и, следовательно, решает выделить достаточно памяти для соответствия всем элементам как set s, но потом он использует только часть этой памяти, тогда как в случае итератора он просто выполняет итераторы над ним и добавляет элементы один за другим (что не потребляет больше памяти, поскольку все элементы уже находятся в set).
Но range также является последовательностью, а также list в первом примере.
>>> len(range(1000))
1000
>>> range(1000)[100]
100
Итак, почему этого не происходит с range и list, но только с set?
Есть ли какое-либо дизайнерское решение за этим или это ошибка?
Протестировано на python 2.7.3 и python 3.2.3 на 64-разрядном Linux.