Как делает коллекции.defaultdict работы?

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

>>> 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 - для чего?

12 ответов


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


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

например:

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

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

defaultdict

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

как определено Даг Хеллманн на стандартная библиотека 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})

в случае, если вы хотите изменить значение по умолчанию перезаписать 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]})

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

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

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


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

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

чтобы уточнить, что означает "вызываемый", вот интерактивный сессия (с 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})
>>> 

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

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

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


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

становится интереснее, когда значения в словаре-это коллекции (списки, дикты и т. д.) В этом случае значение (пустой список или 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.


без 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

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

option = 1

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

нет switch операторы case, доступные в python. Мы можем достичь того же, используя 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 и, следовательно, печатает значение по умолчанию, которое мы настроили в вспомогательной функции. Это довольно лучше, чем raw словарь, где KeyError выбрасывается, если ключа нет. Из этого видно, что defaultdict больше похоже на оператор switch case, где мы можем избежать сложного 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's value by 1
...
defaultdict(<type 'int'>, {'eggs': 1, 'spam': 7})
>>>

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


стандартный словарь включает метод 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']

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

$ python collections_defaultdict.py

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

инструмент defaultdict является контейнером в классе коллекций Python. Он похож на обычный контейнер dictionary (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'])

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

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

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