Python: `ключ не в my_dict`, а` ключ в my_dict.keys () ` - PullRequest
4 голосов
/ 13 сентября 2010

У меня странная ситуация.У меня есть диктат, self.containing_dict.Используя отладочный тест, я вижу содержимое этого dict и вижу, что ключом является self.Но посмотрите на это:

>>> self in self.containing_dict
False
>>> self in self.containing_dict.keys()
True
>>> self.containing_dict.has_key(self)
False

Что происходит?

(отмечу, что это фрагмент кода, который выполняется при обратном вызове со слабой ссылкой.)

Обновление: Меня попросили показать __hash__ реализацию self.Вот оно:

def __hash__(self):
    return hash(
        (
            tuple(sorted(tuple(self.args))),
            self.star_args,
            tuple(sorted(tuple(self.star_kwargs)))
        )
    )

args = property(lambda self: dict(self.args_refs))

star_args = property(
    lambda self:
        tuple((star_arg_ref() for star_arg_ref in self.star_args_refs))
)

star_kwargs = property(lambda self: dict(self.star_kwargs_refs))    

Ответы [ 3 ]

5 голосов
/ 13 сентября 2010

Проблема, которую вы описываете, может быть вызвана только тем, что self внедрил __eq__ (или __cmp__) без реализации сопровождающего __hash__.Если вы не реализовали метод __hash__, вы должны это сделать - обычно вы не можете использовать объекты, которые определяют __eq__, но не __hash__ в качестве ключей, но если вы унаследовали __hash__, который может проскочитьby.

Если вы реализуете __hash__, вы должны убедиться, что он действует правильно: результат не должен изменяться в течение срока службы объекта (или, по крайней мере, до тех пор, пока объект используется).в качестве ключа или элемента), и он должен соответствовать __eq__.Хеш-значение объекта должно совпадать с объектами, которым оно равно (в соответствии с __eq__ или __cmp__.) Хеш-значение объекта может отличаться от объектов, которым оно не равночтобы, но это не должно быть.Требования также означают, что вы не можете иметь результат изменения __eq__ в течение срока службы объекта, поэтому изменяемые объекты обычно нельзя использовать в качестве ключей dict.

Если ваши __hash__ и __eq__ не совпадают, Python не сможет найти объект в комментариях и наборах, но он все равно будет отображаться в dict.keys() и list(set), что вы и описываете здесь.Обычный способ реализовать методы __hash__ - это вернуть hash() любых атрибутов, которые вы используете в своем методе __eq__ или __cmp__.

2 голосов
/ 13 сентября 2010

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

Аргументы не должны быть сложными, подойдет простой список.

In [13]: class Spam(object) :
   ....:     def __init__(self, arg) :
   ....:         self.arg = arg
   ....:     def __hash__(self) :
   ....:         return hash(tuple(self.arg,))

In [18]: l = range(5)

In [19]: spam = Spam(l)

In [20]: hash(spam)
Out[20]: -3958796579502723947

Если я изменю список, который передал в качестве аргумента, хеш изменится.

In [21]: l += [10]

In [22]: hash(spam)
Out[22]: -6439366262097674983

Поскольку ключи словаря организованы по хешу, когда я делаю x in d, первое, что делает Python, - это вычисляет хеш x и ищет в словаре что-то с таким значением хеша. Проблема заключается в том, что когда хеш объекта изменяется после помещения в словарь, Python будет смотреть на новое значение хеша и не будет видеть там нужный ключ. Используя список ключей, вынуждает Python проверять каждый ключ на равенство, минуя проверку хеша.

0 голосов
/ 13 сентября 2010

Скорее всего, у вас есть пользовательский хеш и сравнение, определенное для любого класса. self является экземпляром, и вы изменили self после добавления его в словарь.

Если вы используете изменяемый объект в качестве словарязатем, после изменения, вы не сможете получить к нему доступ в словаре, но он все равно будет отображаться в результате keys().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...