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

Разобравшись с различными темами, связанными с наборами Python, мы теперь внимательно рассмотрим словари в Python и посмотрим, что они могут предложить.

Словарь в Python — это составной тип данных, который на самом деле просто причудливый способ сказать: «коллекция объектов». Теперь мы можем сказать то же самое о списках, кортежах или даже множествах, верно? Но что отличает словари?

Ну, во-первых, стоит отметить, что словари изменяемы. Мы можем изменить элементы по мере необходимости, объект сохранит свое id на протяжении всего процесса изменения.

Во-вторых, они динамичны. Например, как и списки, словари могут увеличиваться и уменьшаться по мере необходимости.

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

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

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

Создать словарь

Это можно сделать одним из двух способов:

  • инициализированный словарь, в этом случае мы напишем что-то вроде my_dict = { 1: "a", 2: "b", 3: "c" } ;
  • пустой словарь, хотя мы не можем использовать синтаксис фигурных скобок, например my_dict = {} , так как это заставит Python думать, что мы создаем набор. Чтобы правильно создать пустой словарь, мы должны использовать форму конструктора: my_dict = dict() :
# initialized dictionary
my_dictionary = {1: "a", 2: "b", 3: "c"}
print(my_dictionary)

# empty dictionary
my_new_dict = dict()
print(my_new_dict)

Output:
{1: 'a', 2: 'b', 3: 'c'}
{}

Словари изменяемы

Вот как мы можем это проверить:

# initialize a dictionary and print it
my_dictionary = {1: "a", 2: "b", 3: "c"}
print(f"{id(my_dictionary)}: {my_dictionary}")

# alter dictionary contents
my_dictionary[4] = "d"
my_dictionary[1] = "e"

# same id, updated contents
print(f"{id(my_dictionary)}: {my_dictionary}")

Итерация по словарю

В зависимости от того, что нам нужно, у нас есть разные сценарии:

# initialize a dictionary
my_dictionary = {1: "a", 2: "b", 3: "c"}

# iterate through the keys
for key in my_dictionary:
    print(f"{key}")

# same thing, different syntax:
for key in my_dictionary.keys():
    print(f"{key}")

# iterate through the values
for value in my_dictionary.values():
    print(f"{value}")

# iterate through the items
for item in my_dictionary.items():
    print(f"{item}")


Output:
1
2
3
1
2
3
a
b
c
(1, 'a')
(2, 'b')
(3, 'c')

Важно принять к сведению тот факт, что подход for item in dictionary будет перебирать ключи по умолчанию. Если мы ищем значения, мы можем либо сделать более конкретное for item in dictionary.values(), либо сделать это вместо этого:

for key in dictionary:
    print(f"{dictionary[key]}")

Затем, что немного интереснее, это вариант использования, в котором нам нужно задействовать метод dictionary.items(). Это, как показано несколькими строками выше, возвращает объект, подобный множеству, состоящий из элементов словаря в виде кортежей, имеющих ключ в качестве первого элемента, за которым следует значение.

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

Как обычно, мы можем сделать это одним из двух способов: поверхностным копированием словаря или глубоким копированием словаря. Разница в том, что неглубокая копия словаря содержит ссылки на элементы скопированного словаря, в то время как глубокая копия также копирует элементы — рекурсивная неглубокая копия, если хотите.

Мелкая копия

Вот несколько способов поверхностного копирования словаря:

# initialize first dictionary
my_dict = {1: {1: "a", 2: "b"}, 2: {3: "c", 4: "d"}}

# shallow copy using dict constructor
my_second_dict = dict(my_dict)

# print them out
print(f"{id(my_dict)}: {my_dict}")
print(f"{id(my_second_dict)}: {my_second_dict}")

# alter the second dict
my_second_dict[2][5] = "e"

# the change is reflected to the first dict
print(f"{id(my_dict)}: {my_dict}")
print(f"{id(my_second_dict)}: {my_second_dict}")


Output:
139631010243008: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
139631008981504: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
139631010243008: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd', 5: 'e'}}
139631008981504: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd', 5: 'e'}}

Мы только что видели, как конструктор dict на самом деле выполняет поверхностную копию словаря. Далее мы увидим, как можно использовать dict.copy() для достижения того же эффекта:

# initialize first dictionary
my_dict = {1: {1: "a", 2: "b"}, 2: {3: "c", 4: "d"}}

# shallow copy using dict.copy() method
my_second_dict = my_dict.copy()

# print them out
print(f"{id(my_dict)}: {my_dict}")
print(f"{id(my_second_dict)}: {my_second_dict}")

# alter the second dict
my_second_dict[2][5] = "e"

# the change is reflected to the first dict
print(f"{id(my_dict)}: {my_dict}")
print(f"{id(my_second_dict)}: {my_second_dict}")


Output:
140086425994560: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
140086426853824: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
140086425994560: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd', 5: 'e'}}
140086426853824: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd', 5: 'e'}}

Глубокая копия

Рекурсивная неглубокая копия, глубокая копия может быть достигнута путем импорта copy, а затем использования метода copy.deepcopy() в целевом словаре:

import copy

# initialize first dictionary
my_dict = {1: {1: "a", 2: "b"}, 2: {3: "c", 4: "d"}}

# deep copy
my_second_dict = copy.deepcopy(my_dict)

# print them out
print(f"{id(my_dict)}: {my_dict}")
print(f"{id(my_second_dict)}: {my_second_dict}")

# alter the second dict
my_second_dict[2][5] = "e"

# the change is not reflected to the first dict
print(f"{id(my_dict)}: {my_dict}")
print(f"{id(my_second_dict)}: {my_second_dict}")


Output:
140697250449024: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
140697250459968: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
140697250449024: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd'}}
140697250459968: {1: {1: 'a', 2: 'b'}, 2: {3: 'c', 4: 'd', 5: 'e'}}

На этот раз изменение коснулось только второго словаря. Первый остается нетронутым. Как я уже говорил ранее, глубокое копирование — это не что иное, как рекурсивный процесс поверхностного копирования, должно быть совершенно очевидно, что глубокое копирование — обычно более медленный процесс, чем поверхностное копирование. В конце концов, все дело в том, что нам нужно: скорость против точности. Использование механизмов копирования во вложенных объектах (таких как вложенные списки или вложенные словари) без полного понимания основной разницы между поверхностным и глубоким копированием может привести к довольно коварным ошибкам в вашем программном обеспечении.

Очистка словаря

Предположим, у нас есть словарь, который содержит некоторые элементы, и по какой-то причине мы хотим, чтобы он был очищен:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}
print(f"{id(my_dict)}: {my_dict}")

# clear the dictionary
my_dict.clear()
print(f"{id(my_dict)}: {my_dict}")


Output:
140374480416704: {1: 'a', 2: 'b', 3: 'c'}
140374480416704: {}

Инициализация словаря с заданными ключами и тем же значением

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

# declare list of keys
my_keys = [1, 2, 3, 4]

# all keys will have to have the value 10
my_value = 10

# create said dictionary and print it
my_dict = dict.fromkeys(my_keys, my_value)
print(f"{id(my_dict)}: {my_dict}")


Output:
139942185766848: {1: 10, 2: 10, 3: 10, 4: 10}

Обратите внимание, что методу fromkeys() не нужен фактический экземпляр словаря. Это статический метод, вызываемый в классе dict. Следует отметить, что аргумент значения является необязательным. Если он не указан, он только инициализирует все указанные ключи значением None.

Получить значение элемента из словаря

Есть два способа добиться этого: более прямой подход или более безопасный от ошибок подход. Прямой подход состоял бы в том, чтобы просто получить доступ к элементу: value = my_dictionary[key]. Проблема с этим подходом заключается в том, что он вызовет исключение KeyError, если key не будет частью словаря.

Второй подход пытается найти ключ для получения соответствующего значения. Если запрошенного ключа нет, он возвращает значение по умолчанию, если оно предоставлено с таким аргументом. Если аргумент значения по умолчанию не указан, он просто возвращает None:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}

# this will return "a"
element = my_dict.get(1, "d")
print(element)

# however, this will return "d"
element = my_dict.get(5, "d")
print(element)


Output:
a
d

Получение элементов словаря, ключей и значений

Метод dictionary.items() вернет все элементы словаря (пары ключ-значение, содержащиеся в словаре). Метод dictionary.keys() вернет все ключи в словаре, а dictionary.values() даст нам все значения в словаре:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}

# print items
print(f"{my_dict.items()}")

# print keys
print(f"{my_dict.keys()}")

# print values
print(f"{my_dict.values()}")


Output:
dict_items([(1, 'a'), (2, 'b'), (3, 'c')])
dict_keys([1, 2, 3])
dict_values(['a', 'b', 'c'])

Извлеките элемент из словаря

Удаление элемента из словаря по существу означает удаление указанной пары ключ-значение. Метод dictionary.pop() сделает это за нас. Нам просто нужно предоставить ему аргумент — ключ, который мы хотим удалить:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}
print(f"{id(my_dict)}: {my_dict}")

# pop the first element and get its value
value = my_dict.pop(1)

print(f"{id(my_dict)}: {my_dict}")
print(value)

# try to pop a nonexistent element
my_dict.pop(0)


Output:
139790660993088: {1: 'a', 2: 'b', 3: 'c'}
139790660993088: {2: 'b', 3: 'c'}
a
Traceback (most recent call last):
  ...
    my_dict.pop(0)
KeyError: 0

Как видно, вызов pop() для элемента, которого нет в словаре, вызывает исключение KeyError.

Есть еще один метод, который позволит нам удалять элементы. Метод dictionary.popitem() удалит последний добавленный элемент. До Python 3.7 этот метод удалял случайный элемент из словаря. Вот краткая демонстрация метода в действии:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}
print(f"{id(my_dict)}: {my_dict}")

# pop and get the last item
item = my_dict.popitem()

print(item)
print(f"{id(my_dict)}: {my_dict}")


Output:
139680705994816: {1: 'a', 2: 'b', 3: 'c'}
(3, 'c')
139680705994816: {1: 'a', 2: 'b'}

Вставьте пару ключ-значение по умолчанию в словарь

Метод dictionary.setdefault() вставит новый элемент в словарь, используя предоставленные аргументы ключа и значения. Но только если его еще нет в словаре. В противном случае значение указанного ключа извлекается из словаря:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}

# try to insert default value for key=1
value = my_dict.setdefault(1, "d")
print(value)
print(f"{id(my_dict)}: {my_dict}")

# try to insert default value for key=4
value = my_dict.setdefault(4, "d")
print(value)
print(f"{id(my_dict)}: {my_dict}")


Output:
a
139972485861440: {1: 'a', 2: 'b', 3: 'c'}
d
139972485861440: {1: 'a', 2: 'b', 3: 'c', 4: 'd'}

Когда ключ существует, он просто действует как подход dictionary.get(key) или dictionary[key], эффективно извлекая значение для указанного ключа. Однако, когда ключ не существует, он не только возвращает указанное значение, но, в отличие от dictionary.get(key, default_value), это также изменяет словарь, эффективно создавая пару ключ-значение и вставляя ее в словарь.

Обновить элемент словаря

Метод dictionary.update() принимает предоставленную пару ключ-значение, затем либо создает элемент, если он не существует, либо просто обновляет значение существующего ключа:

# initialize a dictionary
my_dict = {1: "a", 2: "b", 3: "c"}
print(f"{id(my_dict)}: {my_dict}")

# update the key=1 element
my_dict.update({1: "d"})
print(f"{id(my_dict)}: {my_dict}")

# update the key=4 element
my_dict.update({4: "d"})
print(f"{id(my_dict)}: {my_dict}")


Output:
140049778729024: {1: 'a', 2: 'b', 3: 'c'}
140049778729024: {1: 'd', 2: 'b', 3: 'c'}
140049778729024: {1: 'd', 2: 'b', 3: 'c', 4: 'd'}

Объединить два словаря в один

Это довольно очевидно, хотя и не так очевидно. Мы можем использовать оператор pipe (|) именно для этой цели:

# initialize two dictionaries
my_dict_1 = {"key_1": "value_1"}
my_dict_2 = {"key_2": "value_2"}

# unify them into a third one and print it
my_unified_dict = my_dict_1 | my_dict_2
print(my_unified_dict)

Output:
{'key1': 'value1', 'key2': 'value2'}

И с этим пришло время, чтобы мы завершили и это. Я попытался (и надеюсь, мне это тоже удалось) осветить наиболее часто используемые темы в словарях Python. Я с нетерпением жду возможности поделиться со всеми вами новыми знаниями и до следующего раза желаю вам только самого оглушительного успеха, чем бы вы ни занимались! Ваше здоровье!

Дека — инженер-программист, наставник, писатель, а иногда даже учитель. Обладая более чем 12-летним опытом разработки программного обеспечения, он теперь является настоящим сторонником языка программирования Python, а его страстью является помощь людям в оттачивании их навыков Python и программирования в целом. Вы можете связаться с колодой в Linkedin, Facebook, Twitter и Discord: Deck451#6188, а также следить за его публикациями здесь, на Medium.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.