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


532

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


15
Кстати, в зависимости от вашего варианта использования, не забудьте заморозить defaultdict для использования только для чтения, установив его default_factory = Noneпосле того, как вы закончили заполнять defaultdict. Смотрите этот вопрос .
Acumenus

Ответы:


598

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


4
Это функционально отличается от использования d.get (ключ, default_val)?
Ambareesh

29
@Ambareesh d.get(key, default)никогда не будет изменять ваш словарь - он просто вернет значение по умолчанию и оставит словарь без изменений. defaultdictс другой стороны, вставит ключ в словарь, если его еще нет. Это большая разница; посмотрите примеры в вопросе, чтобы понять, почему.
Свен Marnach

Как мы узнаем, каково значение по умолчанию для каждого типа? 0 для int () и [] для list () интуитивно понятны, но могут быть и более сложные или определяемые пользователем типы.
Шон

1
@Sean defaultdictвызывает любой конструктор, который вы передаете. Если вы передаете тип a T, значения будут создаваться с использованием T(). Не все типы могут быть построены без передачи каких-либо параметров. Если вы хотите создать такой тип, вам нужна функция-оболочка или что-то вроде этого functools.partial(T, arg1, arg2).
Свен Марнах

224

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

Например:

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

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

10
«Тип этой новой пары задается аргументом defaultdict». Обратите внимание, что аргументом может быть любой вызываемый объект, а не только функции типа. Например, если foo была функцией, которая возвращала «bar», foo можно было бы использовать в качестве аргумента для dict по умолчанию, и если был получен доступ к отсутствующему ключу, его значение было бы установлено в «bar».
lf215

13
Или, если вы просто хотите вернуть «бар»: somedict = defaultdict (лямбда: «бар»)
Майкл Скотт Катберт

Четвертая строка возвращается 0целое число, если это someddict = defaultdict(list)возвращается [ ]. 0 является целым числом по умолчанию? Или [] список по умолчанию?
Гатиде

Ни. 0является неизменным - в CPython все значения от -5to 256являются кэшированными одиночками, но это поведение, зависящее от реализации - в обоих случаях новый экземпляр «создается» каждый раз с помощью int()или list(). Таким образом, d[k].append(v)можно работать, не заполняя словарь ссылками на тот же список, что сделало бы defaultdictпрактически бесполезным. Если бы это было поведение defaultdict, в качестве параметра было бы значение, а не лямбда. (Простите за ужасное объяснение!)
wizzwizz4

93

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

20

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

Это становится более интересным, когда значения в словаре являются коллекциями (списки, 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.


18

Здесь есть отличное объяснение дефолтных приговоров: http://ludovf.net/blog/python-collections-defaultdict/

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

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


7

Поскольку вопрос о том, «как это работает», некоторые читатели могут захотеть увидеть больше гаек и болтов. В частности, рассматриваемый метод является __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})
>>> 

7

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

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

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


4

Поведение 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 - это коллекция, которая добавляет больше путаницы, чем преимуществ к коду. Бесполезно для меня, но другие могут думать иначе.


3

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

«Тип данных полей значений указывается при инициализации»: это неверно. Функция фабрики элемента обеспечена. Здесь listвызывается функция для заполнения пропущенного значения, а не тип создаваемых объектов. Например, чтобы иметь значение по умолчанию 1, вы бы использовали, lambda:1который явно не является типом.
asac

2

Я думаю, что его лучше использовать вместо оператора 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'
}

В switchPython нет доступных регистров. Мы можем достичь того же, используя 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выбрасывается a, если ключа нет. Из этого очевидно, что defaultdictбольше похоже на оператор переключения регистра, где мы можем избежать сложных 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.


2

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

2

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

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

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


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']

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

$ python collections_defaultdict.py

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

0

Короче говоря:

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

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


-9

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.