Почему defaultdict регистрирует ключи без назначений - PullRequest
1 голос
/ 26 апреля 2020

В python 3, если вы определяете defaultdict, запускаете значения по умолчанию, которые будут возвращены с несуществующим ключом, тогда этот ключ будет автоматически помещен в словарь ?? Это:

foo = defaultdict(int)
1 in foo # False
foo[1]   # 0
1 in foo # True????

Это кажется мне ошибочным. Я чувствую, что цель defaultdict - дать пользователю возможность получить значение по умолчанию, не помещая этот ключ в словарь. Почему разработчики языка выбрали это и как мне этого избежать?

Ответы [ 2 ]

3 голосов
/ 26 апреля 2020

Дело не в языковом дизайне. Вы можете не использовать collection.defaultdict.

. Вы можете определить свой собственный словарь, который будет работать так, как вам нужно, определив метод __missing__:

>>> class MyDefaultDict(dict):
...     def __init__(self, default_factory):
...         self.default_factory = default_factory
...     def __missing__(self, key):
...         # called by dict.__getitem__ when key is not in the dictionary.
...         return self.default_factory()
... 
>>> 
>>> foo = MyDefaultDict(int)
>>> 1 in foo
False
>>> foo[1]
0
>>> 1 in foo
False

SIDE ПРИМЕЧАНИЕ: defaultdict реализовано с использованием __missing__.

0 голосов
/ 26 апреля 2020

Вы не можете избежать этого, используя defaultdict, поскольку это в его основной реализации:

class defaultdict:

    @staticmethod
    def __new__(cls, default_factory=None, **kwargs):
        # Some code (e.g. urllib.urlparse) expects that basic defaultdict
        # functionality will be available to subclasses without them
        # calling __init__().
        self = super(defaultdict, cls).__new__(cls)
        self.d = {}
        return self

    def __init__(self, default_factory=None, **kwargs):
        self.d = kwargs
        self.default_factory = default_factory

    def __getitem__(self, key):
        try:
            return self.d[key]
        except KeyError:
            v = self.__missing__(key)
            self.d[key] = v
            return v

    def __setitem__(self, key, v):
        self.d[key] = v

    def __delitem__(self, key):
        del self.d[key]

    def __contains__(self, key):
        return key in self.d

    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError(key)
        return self.default_factory()

Вы можете ясно видеть здесь

   def __getitem__(self, key):
        try:
            return self.d[key]
        except KeyError:
            v = self.__missing__(key)
            self.d[key] = v
            return v

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

Это действительно важно в случае, например, при наличии карты списка:

from collections import defaultdict

list_map = defaultdict(list)
list_map['some_key'].append(5)

print(list_map['some_key'])

выведет:

[5]

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

в вашей реализации он будет выводить вместо

[]

Вы можете изменить реализацию, если хотите, это небольшое изменение, но Вы должны рассчитывать на последствия ваших изменений.

...