Почему значение не удаляется из WeakValueDictionary при исчезновении последней ссылки stong - PullRequest
0 голосов
/ 13 января 2020

У меня есть следующая Python программа:

import weakref

class NumberWord:
  def __init__(self, word):
    self.word = word
  def __repr__(self):
    return self.word

dict = weakref.WeakValueDictionary()

print(f"[A] {len(dict)}")
print(f"dict.get(0) = {dict.get(0)}")
print(f"dict.get(1) = {dict.get(1)}")

list = []
list.append(NumberWord("zero"))
dict[0] = list[0]

print(f"[B] {len(dict)}")
print(f"dict.get(0) = {dict.get(0)}")
print(f"dict.get(1) = {dict.get(1)}")

list.append(NumberWord("one"))
dict[1] = list[1]
print(list)

print(f"[C] {len(dict)}")
print(f"dict.get(0) = {dict.get(0)}")
print(f"dict.get(1) = {dict.get(1)}")

list.pop()
print(list)

print(f"[D] {len(dict)}")
print(f"dict.get(0) = {dict.get(0)}")
print(f"dict.get(1) = {dict.get(1)}")

list.pop()
print(list)

print(f"[E] {len(dict)}")
print(f"dict.get(0) = {dict.get(0)}")
print(f"dict.get(1) = {dict.get(1)}")

Я ожидаю следующего поведения:

  • На шаге [A] диктовка пуста

  • На шаге [B] диктовка содержит dict[0] = NumberWord("zero")

  • На шаге [C] диктовка содержит dict[0] = NumberWord("zero") и dict[1] = NumberWord("one")

  • На шаге [D] в диктанте содержится dict[1] = NumberWord("one") («ноль» удален, поскольку единственная сильная ссылка, которая была в списке, исчезла)

  • На шаге [E] диктовка снова пуста («один» удален, потому что единственная сильная ссылка, которая была в списке, исчезла)

Все работает, как ожидалось кроме шаг [E]: «один» делает не go прочь. Почему нет?

Вот фактический результат:

>>> import weakref
>>> 
>>> class NumberWord:
...   def __init__(self, word):
...     self.word = word
...   def __repr__(self):
...     return self.word
... 
>>> dict = weakref.WeakValueDictionary()
>>> 
>>> print(f"[A] {len(dict)}")
[A] 0
>>> print(f"dict.get(0) = {dict.get(0)}")
dict.get(0) = None
>>> print(f"dict.get(1) = {dict.get(1)}")
dict.get(1) = None
>>> 
>>> list = []
>>> list.append(NumberWord("zero"))
>>> dict[0] = list[0]
>>> 
>>> print(f"[B] {len(dict)}")
[B] 1
>>> print(f"dict.get(0) = {dict.get(0)}")
dict.get(0) = zero
>>> print(f"dict.get(1) = {dict.get(1)}")
dict.get(1) = None
>>> 
>>> list.append(NumberWord("one"))
>>> dict[1] = list[1]
>>> print(list)
[zero, one]
>>> 
>>> print(f"[C] {len(dict)}")
[C] 2
>>> print(f"dict.get(0) = {dict.get(0)}")
dict.get(0) = zero
>>> print(f"dict.get(1) = {dict.get(1)}")
dict.get(1) = one
>>> 
>>> list.pop()
one
>>> print(list)
[zero]
>>> 
>>> print(f"[D] {len(dict)}")
[D] 2
>>> print(f"dict.get(0) = {dict.get(0)}")
dict.get(0) = zero
>>> print(f"dict.get(1) = {dict.get(1)}")
dict.get(1) = one
>>> 
>>> list.pop()
zero
>>> print(list)
[]
>>> 
>>> print(f"[E] {len(dict)}")
[E] 1
>>> print(f"dict.get(0) = {dict.get(0)}")
dict.get(0) = zero
>>> print(f"dict.get(1) = {dict.get(1)}")
dict.get(1) = None
>>> 
>>> 

1 Ответ

1 голос
/ 13 января 2020

Я только что обнаружил ответ сам.

Причина в том, что специальная переменная _ по-прежнему содержит результат последней оценки.

Последняя оценка была list.pop(), а ее результат был NumberWord("zero").

Пока этот результат все еще сохраняется в _, у нас будет сильная ссылка, а слабая ссылка не исчезнет go.

Мы можем подтвердить эта теория, вызвав еще одну оценку. В этот момент _ будет содержать другое значение, а слабая ссылка будет go отсутствовать:

Если мы выполним следующие дополнительные операторы в конце примера выше:

_
5 + 5
_
print(f"[F] {len(dict)}")
print(f"dict.get(0) = {dict.get(0)}")
print(f"dict.get(1) = {dict.get(1)}")

Тогда мы получим следующий вывод:

>>> _
zero
>>> 5 + 5
10
>>> _
10
>>> print(f"[F] {len(dict)}")
[F] 0
>>> print(f"dict.get(0) = {dict.get(0)}")
dict.get(0) = None
>>> print(f"dict.get(1) = {dict.get(1)}")
dict.get(1) = None
...