Проверьте, существует ли данный ключ в словаре, и увеличьте его

295

По заданному словарю, как я могу узнать, задан ли для данного ключа в этом словаре значение, отличное от None?

Т.е. я хочу сделать это:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

Т.е. я хочу увеличить значение, если оно уже есть, или установить его на 1 в противном случае.

Бен
источник
11
Небольшой фрагмент кода: код устанавливает для my_dict [key] значение 1, если там уже что-то есть, и увеличивает его, если его там нет. Я думаю, что вы хотите ==, а не! =.
QuantumFool

Ответы:

331

Вы ищете collections.defaultdict(доступно для Python 2.5+). это

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

будет делать то, что вы хотите.

Для обычных Python dict, если для данного ключа нет значения, вы не получите Noneпри доступе к dict - a KeyErrorбудет поднят. Так что если вы хотите использовать обычный dict, вместо вашего кода вы бы использовали

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1
дР.
источник
8
Согласно его примеру, этого должно быть достаточно, чтобы установить «defaultdict (lambda: 0)» и пропустить все предложение «if».
Дестан
Это работает, но путает ключи и значения (что делает его несколько странным для чтения). 'some_value' должно быть 'some_key'
mikemaccana
@nailer: исправлено, спасибо. Первоначально я использовал 'some_value', так как это имя переменной в вопросе, но я согласен, что теперь это понятнее.
ДФ.
20
... или для обычного dictс, вы можете сделать my_dict[key] = my_dict.get(key, 0) + 1.
minmaxavg
Как распространить это на вложенные словари? dict [key1] [key2] + = 1?
Пабло Руис Руис
301

Я предпочитаю делать это в одной строке кода.

my_dict = {}

my_dict [some_key] = my_dict.get (some_key, 0) + 1

В словарях есть функция get, которая принимает два параметра - нужный вам ключ и значение по умолчанию, если оно не существует. Я предпочитаю, чтобы этот метод использовался по умолчанию, поскольку вы хотите обрабатывать только тот случай, когда ключ не существует в этой строке кода, а не везде.

Эндрю Уилкинсон
источник
1
@ AndrewWilkinson мой плохой. Не прочитал твой ответ так тщательно, как следовало бы.
Masaers
59

Мне лично нравится использовать setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1
кичик
источник
setdefaultявляется удивительным. Он не изменяет значение, если оно уже установлено some_key. Например, d={1:2}; d.setdefault(1, 0)не мешает значение d[1].
wsaleem
49

Вам нужна key in dictидиома для этого.

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

Тем не менее, вы, вероятно, должны рассмотреть возможность использования defaultdict(как предложено dF).

Эли Бендерский
источник
1
Обратите внимание, что по крайней мере в 2.6 has_key () был исключен в пользу ключа в d. Я думаю, что так было и в 2.5.
Дэвид Лок
Обратите внимание, что можно написать my_dict[key] is not None, что яснее (по крайней мере, ИМХО)
brandizzi
@brandizzi - согласен,if key in my_dict and my_dict[key]:
Роб Грант
18

Чтобы ответить на вопрос « как я могу узнать, был ли заданный индекс в этом dict уже не равным None », я бы предпочел следующее:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

Это соответствует уже задействованной концепции EAFP (проще просить прощения, чем разрешения). Это также избегает поиска дубликатов ключей в словаре, как это было бы в key in my_dict and my_dict[key] is not Noneтом, что интересно, если поиск дорогой.

Для реальной проблемы, которую вы поставили, то есть для увеличения значения int, если оно существует, или установки значения по умолчанию в противном случае, я также рекомендую

my_dict[key] = my_dict.get(key, default) + 1

как в ответе Эндрю Уилкинсона.

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

my_dict.setdefault(key, []).append(item)

Если значение для ключа не существует в словаре, метод setdefault установит для него второй параметр setdefault. Он ведет себя так же, как стандартный my_dict [ключ], возвращая значение для ключа (которое может быть вновь установленным значением).

без обозначения даты
источник
Pythonic действительно выглядит (для такого постороннего, как я), что на любой вопрос есть как минимум 3 правильных ответа :)
davka
@davka: Хорошо, три варианта использования почти одинаковы, но различаются: а) выяснить, есть ли в словаре элемент, отличный от None; б) извлечь значение из словаря или использовать значение по умолчанию, если значение не существует c) получить значение из словаря и сохранить значение по умолчанию, если значение еще не существует.
nd.
Я знаю :) это не критика, меня просто удивляет этот факт
давка
В комментарии к ответу @ ryeguy Стюарт Вудворд говорит, что «накладные расходы при обработке исключений в языках всегда на порядок больше, чем при поиске в хэш-таблице, определяющей, существует ли элемент в словаре» или нет, а вы говорите: «Это также избегает поиска дубликатов ключей в словаре ... если поиск дорогой "- есть ли у кого-нибудь измерения, где обработка исключений быстрее или медленнее, чем поиск с двойным ключом?
Майкл Ферт
1
@MichaelFirth Я сделал краткий поиск исключений Python: stackoverflow.com/questions/2522005/… это медленнее, но не намного. Имейте в виду, что концепция высокого уровня создания исключения обрабатывается очень по-разному на разных языках, и вы не можете обобщать плюсы и минусы. Таким образом, хотя «исключения имеют 10-кратную служебную нагрузку» могут быть правильными для Java, это не для Python (или Swift или других).
nd.
13

Договорились с cgoldberg. Как я это делаю

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

Так что либо делайте это, как указано выше, либо используйте dict по умолчанию, как предлагали другие. Не используйте операторы if. Это не Pythonic.

ryeguy
источник
8
Как, если заявления не Pythonic?
Адам Паркин
2
Я думаю, что это один из случаев, когда EAFP в Python - не лучший способ. Ваш пример выше имеет дублированный код; что если однажды мы захотим +=2или -=1? Вы должны помнить, чтобы изменить обе строки. Сейчас это может показаться тривиальной вещью, но это те глупые маленькие «тривиальные» ошибки, которые могут вернуться, чтобы укусить вас.
Кэм Джексон
3
Это выглядит хорошо и работает нормально, но я обычно избегаю делать это так, потому что я думал, что накладные расходы при обработке исключений в языках всегда на порядок больше, чем при поиске в хэш-таблице, которая определяет, существует ли элемент в словаре.
Стюарт Вудворд
11

Как видно из множества ответов, существует несколько решений. Один экземпляр LBYL (посмотрите, прежде чем прыгнуть) еще не был упомянут, метод has_key ():

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict
bortzmeyer
источник
6
has_key () медленнее, чем оператор 'in', и менее читабелен.
Абган
9
... и он был объявлен устаревшим в Python 2.6 и удален в Python 3.
Тим Пицкер
7

То, как вы пытаетесь это сделать, называется LBYL (посмотрите, прежде чем прыгнуть), так как вы проверяете условия, прежде чем пытаться увеличить свое значение.

Другой подход называется EAFP (проще просить прощения, чем разрешения). В этом случае вы просто попытаетесь выполнить операцию (увеличить значение). Если это не удается, вы перехватываете исключение и устанавливаете значение 1. Это немного более Pythonic способ сделать это (IMO).

http://mail.python.org/pipermail/python-list/2003-May/205182.html

Кори Голдберг
источник
5

Немного поздно, но это должно сработать.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1
боб
источник
Вау, как программист на Java, это довольно сумасшедшая конструкция. Это похоже на странно упорядоченный троичный оператор?
Форрестхопкинса
5

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

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

Это приводит к выводу

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times
Исаак ван Донген
источник
Счетчик работает так же, как и defaultdict(int)с некоторыми дополнительными функциями, поэтому он будет отлично работать при работе исключительно с целыми числами, но вы не показываете какое-либо соответствующее поведение.
Tadhg McDonald-Jensen
4

Вот одна строка, которую я недавно придумал для решения этой проблемы. Он основан на методе словаря setdefault :

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1
Игорь Гай
источник
0

Я искал это, не нашел его в сети, затем попытал счастья с помощью Try / Error и нашел его

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1
AbhishekKr
источник
1
Вы не должны использовать __contains__в производственном коде. Кстати. __contains__так же, как использование is.
user1767754
1
my_dict.__contains__(some_key)равнозначно some_key in my_dict, перегрузка для inоператора нетis
Tadhg McDonald-Jensen