Словари на Python

Одна из самых важных структур данных в Python

В этой статье я расскажу о словарях. Это вторая статья из серии Структуры данных в Python. Первая часть этой серии была о списках.

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

Эта статья предоставит вам четкое понимание и позволит вам профессионально работать с словарями Python.

В этой статье рассматриваются следующие темы:

  • Создание словаря и добавление элементов
  • Доступ к элементам словаря
  • Удаление элементов словаря
  • Добавление / вставка новых элементов
  • Объединение словарей
  • Изменение словаря
  • Сортировка словаря
  • Понимание словаря
  • Альтернативные способы создания словарей
  • Копирование словаря
  • Переименование существующих ключей
  • Вложенные словари
  • Проверка наличия ключа в словаре

1) Создание словаря и добавление элементов

Подобные списки инициализируются квадратными скобками ([]), словари инициализируются фигурными скобками ({}). Разумеется, пустой словарь имеет нулевую длину.

dic_a = {} # An empty dictionary
type(dic_a)
>>> dict
len(dic_a)
>>> 0

Словарь имеет две характерные особенности: ключи и значения. Каждому ключу соответствует значение. И ключ, и значение могут иметь тип string, float, integer, NaN и т. д. Добавление элементов в словарь означает добавление пары ключ-значение. Словарь состоит из одной или нескольких пар "ключ-значение".

Давайте добавим несколько элементов в наш пустой словарь. Вот один из способов сделать это. Здесь 'A' - ключ, а 'Apple' - его значение. Вы можете добавить столько элементов, сколько захотите.

# Adding the first element
dic_a['A'] = 'Apple'
print (dic_a)
>>> {'A': 'Apple'}
# Adding the second element
dic_a['B'] = 'Ball'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball'}

Примечание. Поскольку Python чувствителен к регистру, 'A' и 'a' действуют как два разных ключа.

dic_a['a'] = 'apple'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'a': 'apple'}

Инициализация словаря сразу

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

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

Гетерогенный словарь

До сих пор в вашем словаре были строки как ключи и значения. В словаре также могут храниться данные смешанного типа. Ниже приведен действующий словарь Python.

dic_b = {1: 'Ace', 'B': 123, np.nan: 99.9, 'D': np.nan, 'E': np.inf}

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

2) Доступ к элементам словаря

Создав наши словари, давайте посмотрим, как мы можем получить доступ к их элементам.

Доступ к ключам и ценностям

Вы можете получить доступ к ключам и значениям, используя функции dict.keys() и dict.values() соответственно. Вы также можете получить доступ как к ключам, так и к значениям в виде кортежей, используя функцию items().

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.keys()
>>> dict_keys(['A', 'B', 'C'])
dic_a.values()
>>> dict_values(['Apple', 'Ball', 'Cat'])
dic_a.items()
>>> dict_items([('A', 'Apple'), ('B', 'Ball'), ('C', 'Cat')])

В качестве альтернативы вы также можете использовать цикл «for» для доступа / печати по одному за раз.

# Printing keys
for key in dic_a.keys():
    print (key, end=' ')
>>> A B C
#############################
# Printing values
for key in dic_a.values():
    print (key, end=' ')
>>> Apple Ball Cat

Вы можете избежать двух циклов «for» и получить доступ к ключам и значениям с помощью items(). Цикл for будет перебирать пары ключ-значение, возвращаемые items(). Здесь key и value - произвольные имена переменных.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
for key, value in dic_a.items():
    print (key, value)
>>> A Apple
    B Ball
    C Cat

Доступ к отдельным элементам

К элементам словаря нельзя получить доступ с помощью индексирования в виде списка.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a[0]
>>> ----> 1 dic_a[0]
    KeyError: 0

Вам нужно использовать ключи для доступа к соответствующим значениям из словаря.

# Accessing the value "Apple"
dic_a['A']
>>> 'Apple'
# Accessing the value "Cat"
dic_a['C']
>>> 'Cat'

Вы получите сообщение об ошибке, если ключ не существует в словаре.

dic_a['Z']
>>> KeyError Traceback (most recent call last)
----> 1 dic_a['Z']
KeyError: 'Z'

Если вы хотите избежать таких ключевых ошибок в случае несуществующих ключей, вы можете использовать функцию get(). Это возвращает None, если ключ не существует. Вы также можете использовать собственное сообщение для возврата.

print (dic_a.get('Z'))
>>> None
# Custom return message
print (dic_a.get('Z', 'Key does not exist'))
>>> Key does not exist

Доступ к элементам словаря как к списку

Если вы хотите получить доступ к элементам словаря (ключам или значениям) с помощью индексов, вам необходимо сначала преобразовать их в списки.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
list(dic_a.keys())[0]
>>> 'A'
list(dic_a.keys())[-1]
>>> 'C'
list(dic_a.values())[0]
>>> 'Apple'
list(dic_a.values())[-1]
>>> 'Cat'

3) Удаление элементов словаря

Удаление элементов из словаря означает одновременное удаление пары ключ-значение.

Используя del

Вы можете удалить элементы словаря, используя ключевое слово del и ключ, значение которого вы хотите удалить. Удаление выполняется на месте, что означает, что вам не нужно повторно назначать значение словаря после удаления.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
# Deleting the key-value pair of 'A': 'Apple'
del dic_a['A']
print (dic_a)
>>> {'B': 'Ball', 'C': 'Cat'}
# Deleting the key-value pair of 'C': 'Cat'
del dic_a['C']
print (dic_a)
>>> {'B': 'Ball'}

Использование pop ()

Вы также можете использовать функцию «pop ()» для удаления элементов. Он возвращает всплывающее (удаляемое) значение, а словарь изменен на месте.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.pop('A')
>>> 'Apple' 
print (dic_a)
# {'B': 'Ball', 'C': 'Cat'}

В обоих описанных выше методах вы получите KeyError, если удаляемый ключ не существует в словаре. В случае «pop ()» вы можете указать сообщение об ошибке, которое будет отображаться, если ключ не существует.

key_to_delete = 'E'
dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.pop(key_to_delete, f'Key {key_to_delete} does not exist.')
>>> 'Key E does not exist.'

Удаление нескольких элементов

Прямого пути нет, но вы можете использовать цикл «for», как показано ниже.

to_delete = ['A', 'C']
dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
for key in to_delete:
    del dic_a[key]
    
print (dic_a)
>>> {'B': 'Ball'}

4) Добавление / вставка новых элементов

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

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a['D'] = 'Dog'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
dic_a['E'] = 'Egg'
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog', 'E': 'Egg'}

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

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a['A'] = 'Adam' # key 'A' already exists with the value 'Apple'
print (dic_a)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat'}

Использование update ()

Вы также можете использовать функцию update() для добавления новой пары "ключ-значение", передав пару в качестве аргумента.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.update({'D': 'Dog'})
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

Функция update() также позволяет добавлять несколько пар ключ-значение одновременно в существующий словарь.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = {'D':'Dog', 'E':'Egg'}
dic_a.update(dic_b)
print(dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog', 'E': 'Egg'}

5) Слияние / объединение словарей

Вы можете объединить два или более словарей, используя оператор распаковки (**), начиная с Python 3.5.

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}
dic_merged = {**dic_a, **dic_b}
print (dic_merged)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

Если вы не хотите создавать новый словарь, а просто хотите добавить dic_b к существующему dic_a, вы можете просто обновить первый словарь, как показано ранее.

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}
dic_a.update(dic_b)
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

Как обрабатываются повторяющиеся ключи при объединении?

Одной из характеристик словарей Python является то, что они не могут иметь повторяющихся ключей, т.е. ключ не может появляться дважды. Итак, что произойдет, если вы объедините два или более словарей, имеющих один или несколько общих ключей.

Ответ заключается в том, что пара "ключ-значение" в последнем объединенном словаре (в порядке объединения) выживет. В следующем примере key'A' существует во всех трех словарях, и, следовательно, последний словарь взял значение из последнего объединенного словаря (dic_c).

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'A': 'Apricot'}
dic_c = {'A': 'Adam', 'E': 'Egg'}
dic_merged = {**dic_a, **dic_b, **dic_c}
print (dic_merged)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat', 'E': 'Egg'}

Слово предостережения

Я только что сказал, что в словарях не может быть дубликатов ключей. Строго говоря, вы можете определить словарь с повторяющимися ключами, но при его печати будет напечатан только последний повторяющийся ключ. Как показано ниже, возвращаются только уникальные ключи, а для дублированного ключа (здесь "A") возвращается только последнее значение.

dic_a = {'A': 'Apple', 'B': 'Ball', 'A': 'Apricot', 'A': 'Assault'}
print (dic_a)
>>> {'A': 'Assault', 'B': 'Ball'}

Более простой способ в Python 3.9+

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

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}
dic_c = dic_a | dic_b
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
# Concatenating more than 2 dictionaries
dic_d = dic_a | dic_b | dic_c

6) Изменение словаря

Если вы хотите изменить значение 'A' с 'Apple' на 'Apricot', вы можете использовать простое присвоение.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a['A'] = 'Apricot'
print (dic_a)
>>> {'A': 'Apricot', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

7) Сортировка словаря

Порядок в словаре не поддерживается. Вы можете отсортировать словарь, используя ключи или значения, используя функцию sorted().

Сортировка по ключам

Если ключи являются строками (алфавитами), они будут отсортированы по алфавиту. В словаре у нас есть два основных элемента: ключи и значения. Следовательно, при сортировке по ключам мы используем первый элемент, то есть ключи, и, следовательно, индекс, используемый в лямбда-функции, равен [0]. Вы можете прочитать этот пост для получения дополнительной информации о лямбда-функциях.

dic_a = {'B': 100, 'C': 10, 'D': 90, 'A': 40}
sorted(dic_a.items(), key=lambda x: x[0])
>>> [('A', 40), ('B', 100), ('C', 10), ('D', 90)]

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

# The dictionary remains unordered if you print it
print (dic_a)
>>> {'B': 100, 'C': 10, 'D': 90, 'A': 40}

Если вы хотите отсортировать в обратном порядке, укажите ключевое слово reverse=True.

sorted(dic_a.items(), key=lambda x: x[0], reverse=True)
>>> [('D', 90), ('C', 10), ('B', 100), ('A', 40)]

Сортировка по значениям

Чтобы отсортировать словарь по его значениям, вам нужно использовать индекс «[1]» внутри лямбда-функции.

dic_a = {'B': 100, 'C': 10, 'D': 90, 'A': 40}
sorted(dic_a.items(), key=lambda x: x[1])
>>> [('C', 10), ('A', 40), ('D', 90), ('B', 100)]

8) понимание словаря

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

dic_c = {i: i**2 for i in range(5)}
print (dic_c)
>>> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Если вы хотите, чтобы ваши ключи были строками, вы можете использовать «f-строки».

dic_c = {f'{i}': i**2 for i in range(5)}
print (dic_c)
>>> {'0': 0, '1': 1, '2': 4, '3': 9, '4': 16}

9) Альтернативные способы создания словарей

Создание словаря из списков

Предположим, у вас есть два списка, и вы хотите создать из них словарь. Самый простой способ - использовать конструктор dict().

names = ['Sam', 'Adam', 'Tom', 'Harry']
marks = [90, 85, 55, 70]
dic_grades = dict(zip(names, marks))
print (dic_grades)
>>> {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}

Вы также можете объединить два списка вместе и создать словарь, используя «понимание словаря», как показано ранее.

dic_grades = {k:v for k, v in zip(names, marks)}
print (dic_grades)
>>> {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}

Передача пар ключ-значение

Вы также можете передать список пар ключ-значение, разделенных запятыми, в конструкцию dict(), и она вернет словарь.

dic_a = dict([('A', 'Apple'), ('B', 'Ball'), ('C', 'Cat')])
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

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

dic_a = dict(A='Apple', B='Ball', C='Cat')
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

10) Копирование словаря

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

Присвоение справки

Когда вы просто переназначаете существующий словарь (родительский словарь) на новый словарь, оба они указывают на один и тот же объект («присвоение ссылки»).

Рассмотрим следующий пример, в котором вы переназначаете dic_a на dic_b.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a # Simple reassignment (Reference assignment)

Теперь, если вы измените dic_b (например, добавив новый элемент), вы заметите, что изменение также будет отражено в dic_a.

dic_b['D'] = 'Dog'
print (dic_b)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

Мелкая копия

Неглубокая копия создается с помощью функции copy(). В неглубокой копии два словаря действуют как два независимых объекта, а их содержимое по-прежнему использует одну и ту же ссылку. Если вы добавите новую пару «ключ-значение» в новый словарь (неглубокая копия), она не будет отображаться в родительском словаре.

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a.copy()
dic_b['D'] = 'Dog'
# New, shallow copy, has the new key-value pair
print (dic_b)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
# The parent dictionary does not have the new key-value pair
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

Теперь, если содержимое в родительском словаре («dic_a») изменится, это зависит от типа значения. Например, в дальнейшем содержимое представляет собой простые неизменяемые строки. Таким образом, изменение значения в «dic_b» для данного ключа ('A' в данном случае) не изменит значение ключа 'A' в «dic_a».

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a.copy()
# Replace an existing key with a new value in the shallow copy
dic_b['A'] = 'Adam'
print (dic_b)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat'}
# Strings are immutable so 'Apple' doesn't change to 'Adam' in dic_a
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

Однако, если значение ключа 'A' в «dic_a» является списком, то изменение его значения в «dic_b» отразит изменения в «dic_a» (родительский словарь), поскольку списки изменяемы.

dic_a = {'A': ['Apple'], 'B': 'Ball', 'C': 'Cat'}
# Make a shallow copy
dic_b = dic_a.copy()
dic_b['A'][0] = 'Adam'
print (dic_b)
>>> {'A': ['Adam'], 'B': 'Ball', 'C': 'Coal'}
# Lists are mutable so the changes get reflected in dic_a too
print (dic_a)
>>> {'A': ['Adam'], 'B': 'Ball', 'C': 'Cat'}

11) Переименование существующих ключей

Предположим, вы хотите заменить ключ «Адам» на «Алекс». Вы можете использовать функцию pop, потому что она удаляет переданный ключ (здесь «Адам») и возвращает удаленное значение (здесь 85). Таким образом, вы убиваете двух птиц одним выстрелом. Используйте возвращенное (удаленное) значение, чтобы присвоить значение новому ключу (здесь «Alex»). Могут быть более сложные случаи, когда ключом является кортеж. Такие случаи выходят за рамки данной статьи.

dic_a = {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}
dic_a['Alex'] = dic_a.pop('Adam')
print (dic_a)
>>> {'Sam': 90, 'Tom': 55, 'Harry': 70, 'Alex': 85}

12) Вложенные словари

Вложенный словарь содержит один или несколько словарей в словаре. Ниже приводится простейший пример вложенного словаря с двумя уровнями вложенности. Здесь внешний словарь (уровень 1) имеет только одну пару ключ-значение. Однако теперь значение - это сам словарь.

dic_a = {'A': {'B': 'Ball'}}
dic_a['A']
>>> {'B': 'Ball'}
type(dic_a['A'])
>>> dict

Если вы хотите получить дополнительный доступ к паре «ключ-значение» внутреннего словаря (уровень 2), теперь вам нужно использовать dic_a['A'] в качестве словаря.

dic_a['A']['B']
>>> 'Ball'

Трехслойный словарь

Добавим дополнительный слой вложенного словаря. Теперь dic_a['A'] сам по себе является вложенным словарем, в отличие от простейшего вложенного словаря выше.

dic_a = {'A': {'B': {'C': 'Cat'}}}
# Layer 1
dic_a['A']
>>> {'B': {'C': 'Cat'}}
# Layer 2
dic_a['A']['B']
>>> {'C': 'Cat'}
# Layer 3
dic_a['A']['B']['C']
>>> 'Cat'

13) Проверка наличия ключа в словаре

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

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}
'A' in dic_a
# True
'E' in dic_a
# False

В приведенном выше коде вам не нужно использовать «in dic_a.keys ()», потому что «in dic_a» уже просматривает ключи.

На этом я подошел к концу статьи. Вы можете получить доступ к первой части этой серии Структуры данных в Python здесь.