Python обновляет ключ в dict, если он не существует

104

Я хочу вставить пару ключ-значение в dict, если ключ не в dict.keys (). В основном я мог сделать это с помощью:

if key not in d.keys():
    d[key] = value

Но есть ли способ лучше? Или каково питоническое решение этой проблемы?

Сяочэнь Цуй
источник

Ответы:

144

Звонить не нужно d.keys(), поэтому

if key not in d:
    d[key] = value

достаточно. Нет более четкого и понятного метода.

Вы можете обновить снова с помощью dict.get(), что вернет существующее значение, если ключ уже присутствует:

d[key] = d.get(key, value)

но я настоятельно не рекомендую этого делать; это игра в код, затрудняющая обслуживание и читаемость.

Мартейн Питерс
источник
Нужно ли мне синхронизировать это, если у меня несколько потоков, использующих код?
ed22
1
@ ed22 да, потому что он состоит из нескольких инструкций байт-кода, между которыми могут переключаться потоки.
Мартейн Питерс
77

Использование dict.setdefault():

>>> d = {1: 'one'}
>>> d.setdefault(1, '1')
'one'
>>> d    # d has not changed because the key already existed
{1: 'one'}
>>> d.setdefault(2, 'two')
'two'
>>> d
{1: 'one', 2: 'two'}
Mhawke
источник
5
dict.setdefault()действительно следует использовать только при попытке доступа к значению.
Мартейн Питерс
12
@MartijnPieters: какое это имеет значение? Название setdefault()четко описывает, что происходит - то, что он также возвращает значение, не так уж и важно, и немного странно ИМО. Не могли бы вы дать быстрое объяснение или ссылку на него, почему это нежелательно?
mhawke
6
Вы бы использовать его в выражении, ожидающее значение , которое должно существовать, как в группировке цикла: for obj in iterable: d.setdefault(key_for_obj(obj), []).append(obj). В возвращаемом значении нет ничего необычного, в этом весь смысл метода .
Мартейн Питерс
2
Кроме того, он скрывает то, что вы пытаетесь сделать, а именно установить ключ, только если он еще не присутствует. Читаемость важна, просто воспользуйтесь if key not in d:тестом.
Мартейн Питерс
6
@MartijnPieters: Спасибо за объяснение. Прочитав строку документации, setdefault()кажется, что вы ориентируетесь на то, что вы сказали. Использование a defaultdict(list)приведет к более читабельному коду, чем ваш пример ... так что, возможно, он бесполезен, если вам не нужно работать со стандартными словарями. В целом if key not in dпонятнее.
mhawke
22

Начиная с Python 3.9 вы можете использовать оператор | слияния для объединения двух словарей. Правый диктант имеет приоритет:

d = { key: value } | d

Примечание: это создает новый словарь с обновленными значениями.

Ротарети
источник
4
Хотя это интересно, я не думаю, что он более читабелен, чем принятый ответ
Джастин Фурунесс
Для меня просто приятно знать, что теперь есть способ объединить два диктатора
Остин А.
5

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

d = {**{ key: value }, **default_values}

Я тестировал его с наиболее популярным ответом, и в среднем это происходит быстрее, как это видно в следующем примере.

Тест скорости, сравнивающий метод на основе цикла for с пониманием dict с оператором unpack Тест скорости, сравнивающий метод на основе цикла for с пониманием dict с методом оператора распаковки.

если d = default_vals.copy()в первом случае не выполняется copy ( ), то ответ, получивший наибольшее количество голосов, будет быстрее, когда мы достигнем порядка 10**5и выше. Объем памяти у обоих методов одинаков.

CoderKid
источник
1
Тогда зачем вообще это предлагать
Джонатан
1
Мне это решение кажется совершенно верным. Это также позволяет обновлять несколько значений в одном объявлении.
Ротарети,
большое спасибо. Но вроде должно быть{**default_values, **{ key: value }}
osexp2003
0

Согласно приведенным выше ответам метод setdefault () работал у меня.

old_attr_name = mydict.setdefault(key, attr_name)
if attr_name != old_attr_name:
    raise RuntimeError(f"Key '{key}' duplication: "
                       f"'{old_attr_name}' and '{attr_name}'.")

Хотя это решение не является универсальным. Как раз меня устроил в этом конкретном случае. Точное решение - проверка keyпервого (как уже было рекомендовано), но setdefault()мы избегаем одного дополнительного поиска в словаре, который хоть и небольшой, но все же дает прирост производительности.

user10815638
источник