{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Программирование и анализ данных\n", "*Алла Тамбовцева, НИУ ВШЭ*\n", "\n", "*Данный ноутбук частично основан на [лекциях](http://math-info.hse.ru/s17/1) Щурова И.В., курс «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ).*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Кортежи и словари\n", "\n", "## Кортежи (tuples)\n", "\n", "Кортежи встречаются не только в программировании, но и в математике. В математике под кортежем обычно понимают упорядоченную совокупность элементов, то есть совокупность, порядок элементов которой фиксирован. В кортеже мы точно знаем, какой элемент является первым, какой – вторым, и так далее.\n", "\n", "Внешне кортежи несильно отличаются от списков. Единственное внешнее отличие – элементы кортежа заключаются в круглые, а не в квадратные скобки." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "my_tuple = (1, 2, 4, 6, 9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "К элементам кортежа можно обращаться точно так же, как к элементам списка:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_tuple[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Но, несмотря на кажущееся сходство, кортежи и списки – принципиально разные объекты. Главное отличие кортежей от списков заключается в том, что кортежи – неизменяемые объекты. Другими словами, изменять элементы кортежа нельзя. Проверим:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "'tuple' object does not support item assignment", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmy_tuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m65\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" ] } ], "source": [ "my_tuple[2] = 65" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Иногда это свойство бывает полезным (некоторая «защита» от изменений), иногда – не очень, но для нас пока важно познакомиться с разными объектами в Python, чтобы потом не удивляться. Ведь многие более продвинутые функции могут возвращать результат или, наоборот, принимать на вход только кортежи или только списки." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "При желании кортеж можно превратить в список:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 4, 6, 9]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(my_tuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "И наоборот:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 2, 3)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tuple([1,2,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если посмотреть на методы, применяемые к кортежам (например, набрать `my_tuple.` и нажать *Tab*), то можно заметить, что методов для кортежей сильно меньше по сравнению с методами для списков. Во многом это связано с тем, что кортеж нельзя изменить. Но вот «склеивать» кортежи, создавая при этом новый, легко:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 3, 7, 8)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1, 3) + (7, 8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Словари (dictionaries)\n", "\n", "Обсуждая словари в Python, удобно проводить аналогию с обычными словарями (бумажными или электронными). Что такое словарь? Перечень из пар: *слово-значение* или *слово-список значений*, если значений несколько. Вот и словарь в Python – это объект, структура данных, которая позволяет хранить пары соответствий.\n", "\n", "Давайте представим, что нам нужно создать словарь, который мы будем использовать для программки к мюзиклу \"Notre Dame de Paris\". Будем записывать в словарь `prog` пары соответствий *герой-актер*." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "prog = {'Gringoire' : 'Pelletier', \n", " 'Frollo' : 'Lavoie', 'Phoebus': 'Fiori'}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Первый элемент в каждой паре (до двоеточия) назвается ключом (*key*), второй элемент в каждой паре (после двоеточия) – значением (*value*). Посмотрим на словарь:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Gringoire': 'Pelletier', 'Frollo': 'Lavoie', 'Phoebus': 'Fiori'}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Обращение к элементам словаря" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как и в случае со списками или кортежами, к элементам словаря можно обращаться. Только выбор элемента производится не по индексу, а по ключу: сначала указываем название словаря, а потом в квадратных скобках – ключ, по которому мы хотим вернуть значение. Например, выясним, кто играет роль Феба:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Fiori'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog['Phoebus']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А что будет, если мы запросим элемент по ключу, которого нет в словаре?" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "ename": "KeyError", "evalue": "'Esmeralda'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprog\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'Esmeralda'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mKeyError\u001b[0m: 'Esmeralda'" ] } ], "source": [ "prog['Esmeralda']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В глубине души Python начинает грустно петь \"Where is she, your Esmeralda?\", но вместо эмоций выдает сухое *KeyError*. Ошибка ключа – ну нет в словаре элемента с ключом Esmeralda! " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь представьте себе такую ситуацию: у нас есть список героев (ключей) и мы хотим в цикле вернуть по ним в фамилии актеров (значения). Какого-то одного из героев нет. Что произойдет? На каком-то этапе Python выдаст ошибку, мы вывалимся из цикла, и на этом наша работа остановится. Обидно, да? Чтобы такого избежать, получать значение по ключу можно другим способом – используя метод `.get()`:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "prog.get('Esmeralda') # ни результата, ни ошибки" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если выведем результат на экран явно, с помощью `print()`, увидим, что в случае, если пары с указанным ключом в словаре нет, Python выдаст значение `None`:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None\n" ] } ], "source": [ "print(prog.get('Esmeralda'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Удобство метода `.get()` заключается в том, что мы сами можем установить, какое значение будет возвращено, в случае, если пары с выбранным ключом нет в словаре. Так, вместо `None` мы можем вернуть строку `Not found`, и ломаться ничего не будет:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Not found'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog.get('Esmeralda', 'Not found')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Возвращаемое значение в случае, если запись с указанным ключом отсутствует в словаре, необязательно должно быть строкой, можно было бы поставить какое-нибудь число или значение `False`:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "99\n", "False\n" ] } ], "source": [ "print(prog.get('Esmeralda', 99))\n", "print(prog.get('Esmeralda', False))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Но недостающий элемент мы всегда можем добавить! " ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Gringoire': 'Pelletier',\n", " 'Frollo': 'Lavoie',\n", " 'Phoebus': 'Fiori',\n", " 'Esmeralda': 'Segara'}" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog['Esmeralda'] = 'Segara'\n", "prog" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Внимание:** Если элемент с указанным ключом уже существует, новый с таким же ключом не добавится! Ключ – это уникальный идентификатор элемента. Если мы добавим в словарь новый элемент с уже существующим ключом, мы просто изменим старый – словари являются изменяемыми объектами. Например, так (изменения в программке `prog`):" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Gringoire': 'Pelletier',\n", " 'Frollo': 'Lavoie',\n", " 'Phoebus': 'Fiori',\n", " 'Esmeralda': 'Noa'}" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog['Esmeralda'] = 'Noa'\n", "prog" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Раз элементами словаря являются пары *ключ-значение*, наверняка есть способ выбрать из словаря ключи и значения отдельно. Действительно, для этого есть методы `.keys()` и `values()`. Вызовем сначала все ключи:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['Gringoire', 'Frollo', 'Phoebus', 'Esmeralda'])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Объект, который мы только что увидели, очень похож на список. Но обычным списком на самом деле не является. Давайте попробуем выбрать первый элемент `prog.keys()`:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "'dict_keys' object does not support indexing", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mkeys\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mprog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkeys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mkeys\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'dict_keys' object does not support indexing" ] } ], "source": [ "keys = prog.keys()\n", "keys[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Не получается! Потому что полученный объект имеет специальный тип `dict_keys`, а не `list`. Но это всегда можно поправить, превратив объект `dict_keys` в список:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Gringoire'" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(keys)[0] # получается!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Аналогичным образом можно работать и со значениями:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_values(['Pelletier', 'Lavoie', 'Fiori', 'Noa'])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog.values()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Словари могут состоять не только из строк, почти любые объекты могут быть ключами и значениями списка (забегая вперед, значениями – любые, ключами – только неизменяемые). Например, можно создать словарь оценок, состоящий из пар целых чисел, *числовой id студента-его оценка*." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "numbers = {1 : 7, 2 : 8, 3 : 9}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Обращаться к элементам мы будем, естественно, без кавычек, так как ключами являются числа:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "numbers[1] # оценка студента с id равным 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Словари могут состоять из элементов смешанного типа. Например, вместо числового id можно явно записать имя студента:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "marks = {\"Петя\": 6, \"Вася\": 9}" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "marks[\"Петя\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ну, и раз уж питоновские словари так похожи на обычные, давайте представим, что у нас есть словарь, где все слова многозначные. Ключом будет слово, а значением ‒ целый список. " ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "my_dict = {'swear' : ['клясться', 'ругаться'], \n", " 'dream' : ['спать', 'мечтать']}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "По ключу мы получим значение в виде списка:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['клясться', 'ругаться']" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_dict['swear']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Так как значением является список, можем отдельно обращаться к его элементам:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'клясться'" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_dict['swear'][0] # первый элемент" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можем пойти дальше и создать словарь, где значениями являются словари! Например, представим, что в некотором сообществе проходят выборы, и каждый участник может проголосовать за любое число кандидатов. Данные сохраняются в виде словаря, где ключами являются имена пользователей, а значениями – пары *кандидат-голос*." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "# '+' - за, '-' - против, 0 - воздержался\n", "votes = {'user1': {'cand1': '+', 'cand2': '-'},\n", " 'user2' : {'cand1': 0, 'cand3' : '+'}} " ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'user1': {'cand1': '+', 'cand2': '-'}, 'user2': {'cand1': 0, 'cand3': '+'}}" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "votes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "По аналогии с вложенными списками по ключам мы сможем обратиться к значению в словаре, который сам является значением в `votes` (да, эту фразу нужно осмыслить):" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'+'" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# берем значение, соответствующее ключу user1, в нем – ключу cand1\n", "votes['user1']['cand1'] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А теперь давайте подумаем, как можно вывести на экран элементы словаря по очереди, в цикле. Первая попытка:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gringoire\n", "Frollo\n", "Phoebus\n", "Esmeralda\n" ] } ], "source": [ "for k in prog:\n", " print(k)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Попытка не совсем удалась: на экран были выведены только ключи. А как вывести пары? \n", "\n", "**Задача:** для каждого героя мюзикла из словаря `prog` вывести на экран сообщение вида\n", "\n", " Fiori plays the role of Phoebus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Решение:** вспомним, что мы можем получать значения по ключу, указывая его в квадратных скобках, и проделаем это для всех ключей в словаре:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pelletier plays the role of Gringoire\n", "Lavoie plays the role of Frollo\n", "Fiori plays the role of Phoebus\n", "Noa plays the role of Esmeralda\n" ] } ], "source": [ "for k in prog:\n", " print(prog[k], \"plays the role of\", k)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Упражнение полезное, но, на самом деле, существует специальный метод `.items()`, который позволяет обращаться сразу к парам элементов:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gringoire Pelletier\n", "Frollo Lavoie\n", "Phoebus Fiori\n", "Esmeralda Noa\n" ] } ], "source": [ "for k, v in prog.items():\n", " print(k, v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для того, чтобы вывести и ключ, и значение, нужно в цикле `for` перечислить две переменные через запятую. И совсем не обязательно называть их `k` и `v` или `key` и `value`; Python сам поймет, что первая переменная соответствует ключу, а вторая ‒ значению. Для примера решим поставленную выше задачу с помощью `items()`." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pelletier plays the role of Gringoire\n", "Lavoie plays the role of Frollo\n", "Fiori plays the role of Phoebus\n", "Noa plays the role of Esmeralda\n" ] } ], "source": [ "for hero, actor in prog.items():\n", " print(actor, \"plays the role of\", hero)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если мы посмотрим на `prog.items()`, мы увидим, что этот объект очень похож на список, состоящий из кортежей:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_items([('Gringoire', 'Pelletier'), ('Frollo', 'Lavoie'), ('Phoebus', 'Fiori'), ('Esmeralda', 'Noa')])" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prog.items()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как и случае с методами `.keys()` и `.values()`, полученный объект не является «обычным» списком. Поэтому при необходимости его нужно будет превратить в список:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('Gringoire', 'Pelletier'),\n", " ('Frollo', 'Lavoie'),\n", " ('Phoebus', 'Fiori'),\n", " ('Esmeralda', 'Noa')]" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(prog.items())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метод `.items()` полезен, когда мы хотим выбирать из словаря значения, удовлетворяющие определенным условиям. Для разнообразия возьмем другой словарь – словарь с парами *студент-оценка*:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "grades = {\"Вася\": 7, \"Петя\" : 9, \"Коля\" : 8, \"Лена\" : 8, \n", " \"Василиса\" : 10}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "И выведем на экран имена тех студентов, у которых оценка равна 8:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Коля\n", "Лена\n" ] } ], "source": [ "for name, grade in grades.items():\n", " if grade == 8:\n", " print(name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Только два человека: Коля и Лена. А как проверить, есть ли в словаре элемент с определенным ключом? Воспользоваться оператором `in`:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"Коля\" in grades.keys()" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"Ваня\" in grades.keys()" ] } ], "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.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }