Как работает collection.defaultdict? - PullRequest
398 голосов
/ 05 мая 2011

Я читал примеры в документации по 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 для чего?

Ответы [ 14 ]

456 голосов
/ 05 мая 2011

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

181 голосов
/ 05 мая 2011

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

Например:

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

someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0
79 голосов
/ 21 марта 2015

defaultdict

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

как определено Doug Hellmann in Стандартная библиотека Python на примере

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

Импорт defaultdict

>>> from collections import defaultdict

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

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

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

>>> 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})

В случае, если вы хотите изменить значение по умолчанию overwrite 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]})
18 голосов
/ 09 июня 2013

Здесь есть отличное объяснение неплатежей: http://ludovf.net/blog/python-collections-defaultdict/

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

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

10 голосов
/ 23 апреля 2018

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

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

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

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

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 голосов
/ 18 апреля 2016

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

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

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

5 голосов
/ 02 декабря 2014

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

Конкретнее, этот ответ показывает, как использовать __missing__(key) на практике: https://stackoverflow.com/a/17956989/1593924

Чтобы прояснить, что означает «вызываемый»Вот интерактивный сеанс (начиная с версии 2.7.6, но он должен работать и в версии 3):

>>> 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})
>>> 
3 голосов
/ 22 февраля 2019

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

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

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

2 голосов
/ 19 декабря 2017

Без 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
1 голос
/ 08 марта 2018

Инструмент 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'])
...