{ "cells": [ { "cell_type": "markdown", "id": "35e7641e", "metadata": {}, "source": [ "# Словари #" ] }, { "cell_type": "markdown", "id": "aa115439", "metadata": {}, "source": [ "На этой лекции мы поговорим про словари.\n", "\n", "Словари являются важнейшей структурой данных в Python'е и они позволяют хранить данные в формате ключ-значение.\n", "\n", "Чтобы определить словарь, нужно использовать литерал фигурные скобки или просто вызвать `dict`." ] }, { "cell_type": "code", "execution_count": 1, "id": "647047ab", "metadata": {}, "outputs": [], "source": [ "empty_dict = {}\n", "empty_dict = dict()" ] }, { "cell_type": "markdown", "id": "b793bf48", "metadata": {}, "source": [ "Если мы хотим определить словарь, в котором уже есть какие-то данные, мы просто пишем наши ключ-значение через двоеточие. Например, словарь collections_map содержит отображение и строк, и списки в листы." ] }, { "cell_type": "code", "execution_count": 2, "id": "ab12602e", "metadata": {}, "outputs": [], "source": [ "collections_map = {\n", " \"mutable\": [\"list\", \"set\", \"dict\"],\n", " \"immutable\": [\"tuple\", \"frozenset\"]\n", "}" ] }, { "cell_type": "markdown", "id": "8f0a511f", "metadata": {}, "source": [ "Чем хороши словари? Они позволяют получить значение по ключу за константное время, очень быстро. Это достигается с помощью алгоритма хеширования. Например, мы можем получить `immutable` лист." ] }, { "cell_type": "code", "execution_count": 3, "id": "ce41b9bf", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['tuple', 'frozenset']\n" ] } ], "source": [ "print(collections_map[\"immutable\"])" ] }, { "cell_type": "markdown", "id": "46286967", "metadata": {}, "source": [ "Если мы попытаемся получить доступ по ключу, которого не существует, у нас Python выдаст нам ошибку KeyError, потому что такого ключа очевидно нет." ] }, { "cell_type": "code", "execution_count": 4, "id": "bfbe0151", "metadata": {}, "outputs": [ { "ename": "KeyError", "evalue": "'irresistible'", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcollections_map\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m\"irresistible\"\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mKeyError\u001b[0m: 'irresistible'" ] } ], "source": [ "print(collections_map[\"irresistible\"])" ] }, { "cell_type": "markdown", "id": "f9f60bb7", "metadata": {}, "source": [ "Однако, часто бывает полезно попробовать взять значение по ключу и в случае неудачи вернуть какое-то дефолтное значение. Для этого есть встроенный метод `get` у словаря, который делает именно это. Например, мы попытались взять ключи `irresistible` и вернули `not found`." ] }, { "cell_type": "code", "execution_count": 5, "id": "975284c8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "not found\n" ] } ], "source": [ "print(collections_map.get(\"irresistible\", \"not found\"))" ] }, { "cell_type": "markdown", "id": "ead7601e", "metadata": {}, "source": [ "Чтобы проверить, содержится ли ключ в словаре, мы используем уже знакомые вам операторы." ] }, { "cell_type": "code", "execution_count": 6, "id": "faa26fae", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"mutable\" in collections_map" ] }, { "cell_type": "markdown", "id": "b3a915d7", "metadata": {}, "source": [ "Так как словарь является изменяемой структурой данных, мы можем добавлять и удалять элементы из него. Например, мы можем определить словарь `beatles_map`, который содержит знаменитых музыкантов и их инструменты, и добавить в него Ринго с ударными, просто используя доступ по ключу." ] }, { "cell_type": "code", "execution_count": 9, "id": "0f6226ae", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Paul': 'Bass', 'John': 'Guitar', 'George': 'Guitar'}\n" ] } ], "source": [ "beatles_map = {\n", " \"Paul\": \"Bass\",\n", " \"John\": \"Guitar\",\n", " \"George\": \"Guitar\",\n", "}\n", "\n", "print(beatles_map)" ] }, { "cell_type": "code", "execution_count": 10, "id": "0a54daf3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Paul': 'Bass', 'John': 'Guitar', 'George': 'Guitar', 'Ringo': 'Drums'}\n" ] } ], "source": [ "beatles_map[\"Ringo\"] = \"Drums\"\n", "\n", "print(beatles_map)" ] }, { "cell_type": "markdown", "id": "6d010fb3", "metadata": {}, "source": [ "Чтобы удалить ключ и значение из словаря, можно использовать уже знакомый вам оператор del, который удаляет в данном случае Джона из нашего словаря." ] }, { "cell_type": "code", "execution_count": 13, "id": "230b9d49", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Paul': 'Bass', 'George': 'Guitar', 'Ringo': 'Drums'}\n" ] } ], "source": [ "del beatles_map[\"John\"]\n", "\n", "print(beatles_map)" ] }, { "cell_type": "markdown", "id": "5d2a6238", "metadata": {}, "source": [ "Также, чтобы добавить какой-то ключ-значение в словарь, можно использовать встроенный метод `update`, который принимает словарь и апдейтит существующий словарь." ] }, { "cell_type": "code", "execution_count": 14, "id": "d2ca1637", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Paul': 'Bass', 'George': 'Guitar', 'Ringo': 'Drums', 'John': 'Guitar'}\n" ] } ], "source": [ "beatles_map.update({\n", " \"John\": \"Guitar\"\n", "})\n", "\n", "print(beatles_map)" ] }, { "cell_type": "markdown", "id": "ffbbe1b1", "metadata": {}, "source": [ "Чтобы удалить ключ-значение из словаря и вернуть значение, можно использовать метод pop, и в данном случае мы удаляем Ринго, и нам возвращаются его ударные." ] }, { "cell_type": "code", "execution_count": 15, "id": "c68f6183", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Drums\n", "{'Paul': 'Bass', 'George': 'Guitar', 'John': 'Guitar'}\n" ] } ], "source": [ "print(beatles_map.pop(\"Ringo\"))\n", "\n", "print(beatles_map)" ] }, { "cell_type": "markdown", "id": "64580aff", "metadata": {}, "source": [ "Часто бывает необходимо не только попробовать проверить, существует ли ключ в словаре, но и в случае неудачи добавить эту новую пару ключ-значение. Для этого есть метод `setdefault`, и давайте посмотрим, как он работает. Мы объявляем `unknown_dict`, который абсолютно пуст, и используя метод `setdefault` пытаемся взять какой-то ключ `key` и в случае неудачи добавим туда `default`." ] }, { "cell_type": "code", "execution_count": 18, "id": "403cdacb", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "default\n" ] } ], "source": [ "unknown_dict = {}\n", "\n", "print(unknown_dict.setdefault(\"key\", \"default\"))" ] }, { "cell_type": "code", "execution_count": 19, "id": "f8dbee49", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'key': 'default'}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unknown_dict" ] }, { "cell_type": "markdown", "id": "8fc6bb60", "metadata": {}, "source": [ "Происходит именно это, у нас в нашем пустом словаре получилось отображение `key`, `default` и `default` вернулся к нам в результате вызова метода `setdefault`.\n", "\n", "Если мы попытаемся вызвать `setdefault` и в качестве дефолтного значения передаём `new_default`, у нас вернётся значение, которое уже лежит в словаре, значение `default`." ] }, { "cell_type": "code", "execution_count": 20, "id": "9b0fa584", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "default\n" ] } ], "source": [ "print(unknown_dict.setdefault(\"key\", \"new_default\"))" ] }, { "cell_type": "markdown", "id": "3c5cf8c7", "metadata": {}, "source": [ "Словари, как и все коллекции, поддерживают протокол итерации, поэтому мы можем итерироваться по словарю, например, с помощью метода `for`." ] }, { "cell_type": "code", "execution_count": 23, "id": "5acefef3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'mutable': ['list', 'set', 'dict'], 'immutable': ['tuple', 'frozenset']}\n" ] } ], "source": [ "print(collections_map)" ] }, { "cell_type": "code", "execution_count": 22, "id": "b4c7bb66", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mutable\n", "immutable\n" ] } ], "source": [ "for key in collections_map:\n", " print(key)" ] }, { "cell_type": "markdown", "id": "74c5246c", "metadata": {}, "source": [ "Обратите внимание, мы итерируемся по ключам словаря. Можем вывести собственно ключи, которые содержатся в нашем словаре. \n", "\n", "Если нам нужно итерироваться не по ключам, как по умолчанию, а по ключам и значениям сразу можно использовать метод словаря `items`, который возвращает ключи и значения. Например, можно выводить в нашем `collections_map` как ключи, так и значения." ] }, { "cell_type": "code", "execution_count": 24, "id": "342baa0c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mutable: ['list', 'set', 'dict']\n", "immutable: ['tuple', 'frozenset']\n" ] } ], "source": [ "for key, value in collections_map.items():\n", " print(f\"{key}: {value}\")" ] }, { "cell_type": "markdown", "id": "87b8d55f", "metadata": {}, "source": [ "Если нужно итерироваться по значениям, используйте логично метод `values`, который возвращает именно значения. Также существует симметричный метод `keys`, который возвращает оператор ключей." ] }, { "cell_type": "code", "execution_count": 25, "id": "56344c1b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'set', 'dict']\n", "['tuple', 'frozenset']\n" ] } ], "source": [ "for value in collections_map.values():\n", " print(value)" ] }, { "cell_type": "code", "execution_count": 26, "id": "3299f648", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mutable\n", "immutable\n" ] } ], "source": [ "for key in collections_map.keys():\n", " print(key)" ] }, { "cell_type": "markdown", "id": "d96b364e", "metadata": {}, "source": [ "Важная особенность словарей в Python'е - они содержат ключи и значения в неупорядоченном виде, то есть вы не можете гарантировать, что ключи, например, отсортированы, или хранятся в том порядке, в каком вы их туда добавили. Однако, в Python'е существует `OrderedDict`, это тип, который содержится в модуле `collections`, который гарантирует вам, что ключи содержатся именно в том порядке, в каком вы их добавили в словарь. Например, мы можем вывести пару, число и строка, именно в том порядке, в каком мы их туда добавили." ] }, { "cell_type": "code", "execution_count": 32, "id": "b7ce5f64", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n" ] } ], "source": [ "from collections import OrderedDict\n", "\n", "ordered = OrderedDict()\n", "\n", "for number in range(10):\n", " ordered[number] = str(number)\n", " \n", "for key in ordered:\n", " print(key)" ] }, { "cell_type": "markdown", "id": "8e4e659e", "metadata": {}, "source": [ "По секрету с версии Python 3.8 порядок гарантирован." ] }, { "cell_type": "code", "execution_count": 33, "id": "b885b0f1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n" ] } ], "source": [ "ordered = dict()\n", "\n", "for number in range(10):\n", " ordered[number] = str(number)\n", " \n", "for key in ordered:\n", " print(key)" ] }, { "cell_type": "markdown", "id": "fb29decb", "metadata": {}, "source": [ "Итак, на этой лекции мы обсудили важнейшую структуру данных - словари, которые содержат в неупорядоченном виде набор пар ключ-значение. Вы можете изменять эту структуру данных, потому что она является изменяемой. Словари позволяют вам получать значение по ключу очень быстро за константное время и точно также очень быстро проверять вхождение какого-то ключа в словарь. Это достигается с помощью алгоритмов хеширования. Давайте попробуем решить задачку на словари." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" } }, "nbformat": 4, "nbformat_minor": 5 }