Как работает collection.defaultdict?

Я читал примеры в документах python, но до сих пор не могу понять, что означает этот метод. Может ли кто-нибудь помочь? Вот два примера из документов python

>>> from collections import defaultdict

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]

и

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

параметры int и list для чего?

Ответ 1

Обычно словарь Python выдает KeyError если вы пытаетесь получить элемент с ключом, которого в данный момент нет в словаре. defaultdict просто создаст любые элементы, к которым вы пытаетесь получить доступ (если, конечно, они еще не существуют). Чтобы создать такой элемент "по умолчанию", он вызывает объект функции, который вы передаете конструктору (точнее, это произвольный "вызываемый" объект, который включает объекты функций и типов). В первом примере элементы по умолчанию создаются с использованием int(), которая возвращает объект целого числа 0. Во втором примере элементы по умолчанию создаются с помощью list(), который возвращает новый пустой объект списка.

Ответ 2

defaultdict означает, что если ключ не найден в словаре, то вместо созданного KeyError создается новая запись. Тип этой новой записи задается аргументом defaultdict.

Например:

somedict = {}
print(somedict[3]) # KeyError

someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0

Ответ 3

defaultdict

"Стандартный словарь включает метод setdefault() для извлечения значения и установки значения по умолчанию, если значение не существует. В отличие от этого, defaultdict позволяет вызывающему абоненту указать значение по умолчанию (значение, которое будет возвращено) спереди, когда контейнер инициализируется."

как определено Дугом Хеллманом в стандартной библиотеке Python по Примеру

Как использовать defaultdict

Импортировать defaultdict

>>> from collections import defaultdict

Инициализировать defaultdict

Инициализировать его, передав

callable в качестве первого аргумента (обязательно)

>>> d_int = defaultdict(int)
>>> d_list = defaultdict(list)
>>> def foo():
...     return 'default value'
... 
>>> d_foo = defaultdict(foo)
>>> d_int
defaultdict(<type 'int'>, {})
>>> d_list
defaultdict(<type 'list'>, {})
>>> d_foo
defaultdict(<function foo at 0x7f34a0a69578>, {})

** kwargs в качестве второго аргумента (необязательно)

>>> d_int = defaultdict(int, a=10, b=12, c=13)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

или

>>> kwargs = {'a':10,'b':12,'c':13}
>>> d_int = defaultdict(int, **kwargs)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

Как это работает

Как и дочерний класс стандартного словаря, он может выполнять все те же функции.

Но в случае передачи неизвестного ключа он возвращает значение по умолчанию вместо ошибки. Для примера:

>>> d_int['a']
10
>>> d_int['d']
0
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12, 'd': 0})

Если вы хотите изменить значение по умолчанию, перепишите default_factory:

>>> d_int.default_factory = lambda: 1
>>> d_int['e']
1
>>> d_int
defaultdict(<function <lambda> at 0x7f34a0a91578>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0})

или

>>> def foo():
...     return 2
>>> d_int.default_factory = foo
>>> d_int['f']
2
>>> d_int
defaultdict(<function foo at 0x7f34a0a0a140>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0, 'f': 2})

Примеры в вопросе

Пример 1

Поскольку int передан как default_factory, любой неизвестный ключ по умолчанию возвращает 0.

Теперь, когда строка передается в цикле, она увеличит количество этих алфавитов в d.

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> d.default_factory
<type 'int'>
>>> for k in s:
...     d[k] += 1
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
>>> d
defaultdict(<type 'int'>, {'i': 4, 'p': 2, 's': 4, 'm': 1})

Пример 2

Как список был принят как default_factory, любой неизвестный (несуществующий) ключ по умолчанию возвращает [] (т.е. список).

Теперь, когда список кортежей передается в цикле, он добавит значение в d [color]

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> d.default_factory
<type 'list'>
>>> for k, v in s:
...     d[k].append(v)
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
>>> d
defaultdict(<type 'list'>, {'blue': [2, 4], 'red': [1], 'yellow': [1, 3]})

Ответ 4

Здесь есть большое объяснение defaultdicts: http://ludovf.net/blog/python-collections-defaultdict/

В основном, параметры int и список - это функции, которые вы передаете. Помните, что Python принимает имена функций в качестве аргументов. int возвращает 0 по умолчанию, а list возвращает пустой список при вызове с круглыми скобками.

В нормальных словарях, если в вашем примере я попробую позвонить d[a], я получу ошибку (KeyError), поскольку существуют только ключи m, s, я и p и ключ a не инициализирован. Но в defaultdict оно принимает имя функции в качестве аргумента, когда вы пытаетесь использовать ключ, который не был инициализирован, он просто вызывает функцию, в которую вы передали, и присваивает ее возвращаемое значение в качестве значения нового ключа.

Ответ 5

Словари - это удобный способ хранения данных для последующего поиска по имени (ключу). Ключи должны быть уникальными, неизменяемыми объектами и обычно являются строками. Значения в словаре могут быть любыми. Для многих приложений значения являются простыми типами, такими как целые числа и строки.

Это становится более интересным, когда значения в словаре являются коллекциями (списки, dict и т.д.). В этом случае значение (пустой список или dict) должно быть инициализировано при первом использовании данного ключа. Хотя это относительно легко сделать вручную, тип defaultdict автоматизирует и упрощает подобные операции. Defaultdict работает точно так же, как обычный dict, но он инициализируется функцией ("фабрика по умолчанию"), которая не принимает аргументов и предоставляет значение по умолчанию для несуществующего ключа.

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

from collections import defaultdict
ice_cream = defaultdict(lambda: 'Vanilla')

ice_cream['Sarah'] = 'Chunky Monkey'
ice_cream['Abdul'] = 'Butter Pecan'

print(ice_cream['Sarah'])
>>>Chunky Monkey

print(ice_cream['Joe'])
>>>Vanilla

Вот еще один пример того, как с помощью defaultdict мы можем уменьшить сложность

from collections import defaultdict
# Time complexity O(n^2)
def delete_nth_naive(array, n):
    ans = []
    for num in array:
        if ans.count(num) < n:
            ans.append(num)
    return ans

# Time Complexity O(n), using hash tables.
def delete_nth(array,n):
    result = []
    counts = defaultdict(int)

    for i in array:
        if counts[i] < n:
            result.append(i)
            counts[i] += 1
    return result


x = [1,2,3,1,2,1,2,3]
print(delete_nth(x, n=2))
print(delete_nth_naive(x, n=2))

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

Ответ 6

Поскольку вопрос о том, "как это работает", некоторые читатели могут захотеть увидеть больше гаек и болтов. В частности, рассматриваемый метод представляет собой метод __missing__(key). См.: https://docs.python.org/2/library/collections.html#defaultdict-objects.

Более конкретно, этот ответ показывает, как использовать __missing__(key) на практике: fooobar.com/questions/28752/...

Чтобы уточнить, что означает "вызываемый", здесь интерактивный сеанс (из 2.7.6, но должен работать и в v3):

>>> x = int
>>> x
<type 'int'>
>>> y = int(5)
>>> y
5
>>> z = x(5)
>>> z
5

>>> from collections import defaultdict
>>> dd = defaultdict(int)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd = defaultdict(x)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd['a']
0
>>> dd
defaultdict(<type 'int'>, {'a': 0})

Это было наиболее типичное использование defaultdict (за исключением бессмысленного использования переменной x). Вы можете сделать то же самое с 0 как явное значение по умолчанию, но не с простым значением:

>>> dd2 = defaultdict(0)

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    dd2 = defaultdict(0)
TypeError: first argument must be callable

Вместо этого следующее работает, потому что оно проходит в простой функции (он создает на лету безымянную функцию, которая не принимает аргументов и всегда возвращает 0):

>>> dd2 = defaultdict(lambda: 0)
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {})
>>> dd2['a']
0
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {'a': 0})
>>> 

И с другим значением по умолчанию:

>>> dd3 = defaultdict(lambda: 1)
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {})
>>> dd3['a']
1
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {'a': 1})
>>> 

Ответ 7

Мой собственный 2 ¢: вы также можете подкласса defaultdict:

class MyDict(defaultdict):
    def __missing__(self, key):
        value = [None, None]
        self[key] = value
        return value

Это может пригодиться для очень сложных случаев.

Ответ 8

Инструмент defaultdict - это контейнер в классе коллекций Python. Он похож на обычный контейнер словаря (dict), но имеет одно отличие: тип данных полей значений указывается при инициализации.

Например:

from collections import defaultdict

d = defaultdict(list)

d['python'].append("awesome")

d['something-else'].append("not relevant")

d['python'].append("language")

for i in d.items():

    print i

Это печатает:

('python', ['awesome', 'language'])
('something-else', ['not relevant'])

Ответ 9

Я думаю, что он лучше всего используется вместо оператора case switch. Представьте, если мы имеем оператор case switch, как показано ниже:

option = 1

switch(option) {
    case 1: print '1st option'
    case 2: print '2nd option'
    case 3: print '3rd option'
    default: return 'No such option'
}

В python нет операторов case switch. Мы можем добиться того же, используя defaultdict.

from collections import defaultdict

def default_value(): return "Default Value"
dd = defaultdict(default_value)

dd[1] = '1st option'
dd[2] = '2nd option'
dd[3] = '3rd option'

print(dd[4])    
print(dd[5])    
print(dd[3])

Он печатает:

Default Value
Default Value
3rd option

В приведенном выше фрагменте dd нет клавиш 4 или 5 и, следовательно, он выводит значение по умолчанию, которое мы сконфигурировали в вспомогательной функции. Это намного лучше, чем исходный словарь, где KeyError выбрасывается, если ключ отсутствует. Из этого видно, что defaultdict больше похож на оператор case switch, где мы можем избежать сложных блоков if-elif-elif-else.

Еще один хороший пример, который сильно меня впечатлил от этого сайта:

>>> from collections import defaultdict
>>> food_list = 'spam spam spam spam spam spam eggs spam'.split()
>>> food_count = defaultdict(int) # default value of int is 0
>>> for food in food_list:
...     food_count[food] += 1 # increment element value by 1
...
defaultdict(<type 'int'>, {'eggs': 1, 'spam': 7})
>>>

Если мы попытаемся получить доступ к любым элементам, отличным от eggs и spam, мы получим счетчик 0.

Ответ 10

Без defaultdict вы, вероятно, можете назначить новые значения невидимым ключам, но вы не можете изменить их. Например:

import collections
d = collections.defaultdict(int)
for i in range(10):
  d[i] += i
print(d)
# Output: defaultdict(<class 'int'>, {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9})

import collections
d = {}
for i in range(10):
  d[i] += i
print(d)
# Output: Traceback (most recent call last): File "python", line 4, in <module> KeyError: 0

Ответ 11

Ну, defaultdict также может вызвать keyerror в следующем случае:

    from collections import defaultdict
    d = defaultdict()
    print(d[3]) #raises keyerror

Всегда не забывайте указывать аргумент defaultdict как defaultdict (int).

Ответ 12

Поведение defaultdict можно легко имитировать, используя dict.setdefault вместо d[key] в каждом вызове.

Другими словами, код:

from collections import defaultdict

d = defaultdict(list)

print(d['key'])                        # empty list []
d['key'].append(1)                     # adding constant 1 to the list
print(d['key'])                        # list containing the constant [1]

эквивалентно:

d = dict()

print(d.setdefault('key', list()))     # empty list []
d.setdefault('key', list()).append(1)  # adding constant 1 to the list
print(d.setdefault('key', list()))     # list containing the constant [1]

Единственное отличие состоит в том, что при использовании defaultdict конструктор списка вызывается только один раз, а при использовании dict.setdefault конструктор списка вызывается чаще (но код может быть переписан во избежание этого, если это действительно необходимо).

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

ИМО, defaultdict - это коллекция, которая добавляет больше путаницы, чем преимуществ к коду. Бесполезно для меня, но другие могут думать иначе.

Ответ 13

Короче:

defaultdict(int) - аргумент int указывает, что значения будут типа int.

defaultdict(list) - список аргументов указывает, что значения будут иметь тип списка.

Ответ 14

Стандартный словарь включает метод setdefault() для извлечения значения и установления значения по умолчанию, если значение не существует. Напротив, defaultdict позволяет вызывающему абоненту указывать начальный фронт по умолчанию при инициализации контейнера.

import collections

def default_factory():
    return 'default value'

d = collections.defaultdict(default_factory, foo='bar')
print 'd:', d
print 'foo =>', d['foo']
print 'bar =>', d['bar']

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

$ python collections_defaultdict.py

d: defaultdict(<function default_factory at 0x100468c80>, {'foo': 'bar'})
foo => bar
bar => default value

Ответ 15

Документация и объяснение в значительной степени не требуют пояснений:

http://docs.python.org/library/collections.html#collections.defaultdict

Функция типа (int/str и т.д.), переданная в качестве аргумента, используется для инициализации значения по умолчанию для любого заданного ключа, где ключ отсутствует в dict.