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


109

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

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

Але чи є кращий спосіб? Або яке пітонічне рішення цієї проблеми?


Відповіді:


153

Вам не потрібно телефонувати d.keys(), тому

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

достатньо. Немає більш чіткого, читабельного методу.

Ви можете знову оновити за допомогою dict.get(), яке поверне існуюче значення, якщо ключ уже присутній:

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

але я настійно не рекомендую цього; це кодовий гольф, що заважає технічному обслуговуванню та читабельності.


Чи потрібно синхронізувати це, якщо у мене є кілька потоків, що використовують код?
ed22,

1
@ ed22 так, оскільки він складається з декількох інструкцій байт-коду, між якими потоки можуть перемикатися.
Мартін Пітерс

84

Використання 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'}

6
dict.setdefault()насправді слід використовувати лише під час спроби отримати доступ до значення.
Мартін Пітерс

14
@MartijnPieters: чому це важливо? Назва setdefault()чітко описує те, що відбувається - те, що воно також повертає значення, не таке важливе, і трохи химерне IMO. Не могли б ви дати коротке пояснення або посилання на таке, чому це не бажано?
mhawke

6
Ви б використовувати його в вираженні, що очікує значення , яке має існувати, як в угрупованні циклу: for obj in iterable: d.setdefault(key_for_obj(obj), []).append(obj). У значенні, що повертається, немає нічого химерного, у цьому вся суть методу .
Мартін Пітерс

2
Крім того, це затьмарює те, що ви намагаєтеся зробити, тобто встановити ключ, лише якщо його ще немає. Читаність підраховується, просто скористайтеся if key not in d:тестом.
Мартін Пітерс

7
@MartijnPieters: Дякую за пояснення. Прочитавши документацію, setdefault()схоже, орієнтований на те, що ви сказали. Використання defaultdict(list)коду призведе до читабельнішого коду, ніж ваш приклад ... тому, можливо, від нього немає ніякої користі, якщо не потрібно працювати зі стандартними словниками. Загалом, if key not in dце зрозуміліше.
mhawke

25

Починаючи з Python 3.9, ви можете використовувати оператор | злиття для об’єднання двох словників. Справа праворуч має пріоритет:

new_dict = old_dict | { key: val }

Наприклад:

new_dict = { 'a': 1, 'b': 2 } | { 'b': 42 }

print(new_dict} # {'a': 1, 'b': 42}

Примітка: це створює новий словник із оновленими значеннями.


6
Хоча це цікаво, я не думаю, що це читабельніше прийнятої відповіді
Джастін Фурунесс,

2
Для мене просто приємно знати, що зараз є спосіб об’єднати два дикти
Остін,

З цим об’єднанням виникає проблема. Якщо ключі однакові, злиття ігноруватиме значення. Якщо це намір, то це працює.
Джо Ферндц,

1
@JoeFerndz Знак праворуч має пріоритет і не ігнорує значення. Я оновив приклад, щоб зробити це більш зрозумілим.
Ротарети

6

Далі ви можете вставити кілька значень, а також мати значення за замовчуванням, але ви створюєте новий словник.

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

Я протестував його з найбільш голосованою відповіддю, і в середньому це швидше, як це видно з наступного прикладу,.

Тест швидкості порівняння методу на основі циклу for з розумінням дикту з оператором розпакування Тест швидкості порівняння методу на основі циклу з розумінням дикту з методом оператора розпакування.

якщо d = default_vals.copy()в першому випадку не буде зроблено копії ( ), тоді відповідь, за якою проголосували найбільше, буде швидшою, як тільки ми досягнемо порядків 10**5і більше. Слід пам'яті обох методів однаковий.


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()ми уникаємо одного додаткового пошуку в словнику, тобто хоч і невеликого, але все ж підвищення продуктивності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.