Как «k in d» может быть False, но «k in d.keys ()» может быть True? - PullRequest
7 голосов
/ 27 октября 2010

У меня есть некоторый код Python, который выдает исключение KeyError. Пока я не смог воспроизвести вне операционной среды, поэтому я не могу опубликовать сокращенный тестовый пример здесь.

Код, который вызывает исключение, повторяет цикл следующим образом:

for k in d.keys():
    if condition:
        del d[k]

Строка del[k] выдает исключение. Я добавил предложение try/except вокруг него и смог определить, что k in d - Ложь, но k in d.keys() - Истина.

Ключи d являются связанными методами экземпляров классов старого стиля.

Класс реализует __cmp__ и __hash__, поэтому я сосредоточил свое внимание на этом.

Ответы [ 4 ]

18 голосов
/ 27 октября 2010

k in d.keys() будет проверять равенство итеративно для каждого ключа, в то время как k in d использует __hash__, поэтому ваш __hash__ может быть поврежден (т. Е. Он возвращает разные хэши для объектов, которые сравниваются равными).

5 голосов
/ 27 октября 2010

Простой пример того, что сломано, для интереса:

>>> count = 0
>>> class BrokenHash(object):
...     def __hash__(self):
...             global count
...             count += 1
...             return count
...
...     def __eq__(self, other):
...             return True
...
>>> foo = BrokenHash()
>>> bar = BrokenHash()
>>> foo is bar
False
>>> foo == bar
True
>>> baz = {bar:1}
>>> foo in baz
False
>>> foo in baz.keys()
True
4 голосов
/ 27 октября 2010

Не удаляйте элементы в d во время итерации, храните ключи, которые вы хотите удалить, в списке и удаляйте их в другом цикле:

deleted = []
for k in d.keys():
    if condition:
        deleted.append(k)
for k in deleted:
    del d[k]
0 голосов
/ 27 октября 2010

То, что вы делаете, выдает исключение одновременной модификации в Java. d.keys() создает список ключей в том виде, в котором они существуют, когда вы его вызываете, но этот список теперь статичен - изменения в d не изменят сохраненную версию d.keys(). Поэтому, когда вы перебираете d.keys(), но удаляете элементы, у вас появляется возможность изменить ключ, которого больше нет.

Вы можете использовать d.pop(k, None), который будет возвращать либо значение, сопоставленное с k, либо None, если k отсутствует. Это позволяет избежать проблемы KeyError.

РЕДАКТИРОВАТЬ: Для пояснения, чтобы предотвратить больше фантомных downmods (нет проблем с отрицательной обратной связью, просто сделайте его конструктивным и оставьте комментарий, чтобы мы могли провести потенциально информативное обсуждение - я здесь, чтобы узнать, а также помочь): 1014 *

Это правда, что в этом конкретном состоянии это не должно быть испорчено . Я просто выдвигал это как потенциальную проблему, потому что, если он использует такую ​​же схему кодирования в другой части программы, где он не настолько осторожен / счастлив, как он обрабатывает структуру данных, такие проблемы могут возникнуть. Он даже не использует словарь, а скорее класс, который реализует определенные методы, чтобы вы могли обращаться с ним аналогичным образом.

...