Почему id ({}) == id ({}) и id ([]) == id ([]) в CPython?

Почему CPython (не знает о других реализациях Python) имеет следующее поведение?

tuple1 = ()
tuple2 = ()                                                                                                   
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))

У меня есть несколько идей, почему это возможно, но не может найти конкретной причины.

ИЗМЕНИТЬ

Чтобы дополнительно доказать точку Гленна и Томаса:

[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440

Ответ 1

CPython - сборщик мусора, как только они выходят из области видимости, поэтому второй [] создается после того, как будет собрано первое []. Поэтому большую часть времени он попадает в ту же ячейку памяти.

Это показывает, что происходит очень четко (выход может отличаться в других реализациях Python):

class A(object):
    def __init__(self): print "a",
    def __del__(self): print "b",

# a a b b False
print A() is A()
# a b a b True
print id(A()) == id(A())

Ответ 2

Когда вы вызываете id({}), Python создает dict и передает его функции id. Функция id принимает свой идентификатор (ячейку памяти) и отбрасывает dict. Дик разрушен. Когда вы делаете это дважды в быстрой последовательности (без каких-либо других dicts, создаваемых в среднее время), dict Python создает второй раз, когда используется тот же блок памяти, что и в первый раз. (Распределитель памяти CPython делает это намного более вероятным, чем кажется). Поскольку (в CPython) id использует ячейку памяти в качестве идентификатора объекта, идентификатор двух объектов одинаковый. Это, очевидно, не происходит, если вы назначаете dict переменной, а затем получаете ее id(), потому что dicts живы одновременно, поэтому их id должны быть разными.

Мутируемость напрямую не вступает в игру, но кеширует кеширование кортежей и строк. В том же объекте кода (функция или тело тела тела или тела модуля) будут повторно использоваться те же литералы (целые числа, строки и некоторые кортежи). Переменные объекты никогда не могут быть повторно использованы, они всегда создаются во время выполнения.

Короче говоря, идентификатор объекта уникален только для времени жизни объекта. После уничтожения объекта или до его создания что-то еще может иметь один и тот же идентификатор.

Ответ 3

он не работает одинаково в Jython...

>>> id({})
1
>>> id([])
2

Может ли быть оптимизация, когда обычно используемые (т.е. пустые) контейнеры "интернированы" для экономии на расходах на размещение?

Это (в CPython) не предполагает:

>>> def mutateid(obj):
...   obj.append('x')
...   print obj
...   print id(obj)
... 
>>> mutateid([])
['x']
4299590472
>>> id([])
4299590472
>>> 

Ответ 4

Оператор == на списках и dicts не сравнивает идентификаторы объектов, чтобы увидеть, являются ли они одним и тем же объектом - используйте obj1 is obj2 для этого.

Вместо этого оператор == сравнивает членов списка dict, чтобы проверить, совпадают ли они.