{ "cells": [ { "cell_type": "markdown", "id": "164f2285", "metadata": {}, "source": [ "# Списки и кортежи #" ] }, { "cell_type": "markdown", "id": "a0860ec6", "metadata": {}, "source": [ "В этой лекции мы поговорим с вами о коллекциях.\n", "\n", "Коллекция - это переменная-контейнер, в которой может содержаться какое-то количество объектов, эти объекты могут быть одного типа или разного. В случае списков, о которых далее пойдет речь - это упорядоченные наборы элементов, которые могут быть разных типов. Собственно, списки определяются с помощью квадратных скобочек или с помощью вызова литерала `list`. Вы также можете создать список из одинаковых значений с помощью умножения." ] }, { "cell_type": "code", "execution_count": 34, "id": "50d507c5", "metadata": {}, "outputs": [], "source": [ "empty_list = []\n", "empty_list = list()\n", "\n", "none_list = [None] * 10\n", "\n", "collections = [\"list\", \"tuple\", \"dict\", \"set\"]\n", "\n", "user_data = [\n", " [\"Elena\", 4.4],\n", " [\"Andrey\", 4.2]\n", "]" ] }, { "cell_type": "markdown", "id": "826b2528", "metadata": {}, "source": [ "Чаще всего списки содержат переменные одного типа, например, строки. Однако бывает, что нужно добавлять в списки переменные разных типов. Это тоже возможно. Также списки могут содержать другие коллекции, как, например, `user_data`. Однако для таких данных чаще всего используются кортежи, о которых мы поговорим чуть позже." ] }, { "cell_type": "markdown", "id": "1ca1cebf", "metadata": {}, "source": [ "Чтобы получить длину списка, можно вызвать встроенную функцию `len`." ] }, { "cell_type": "code", "execution_count": 35, "id": "71a1712b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(collections)" ] }, { "cell_type": "markdown", "id": "b8f1100d", "metadata": {}, "source": [ "И, как вы видите, для определения списка не нужно заранее знать длину списка, который вы создаете, списки изменяемые, и Python обо всем заботится о вас. Также интересно, что функция `len` выполняется за константное время, то есть не происходит `full scan`. Python делает все очень быстро." ] }, { "cell_type": "markdown", "id": "81b54f7f", "metadata": {}, "source": [ "Чтобы получить доступ к какому-то конкретному элементу списка, мы обращаемся по индексу к нему, также как и в строчках. Например, чтобы получить первый элемент, мы обращаемся по нулевому индексу, так как списки нумеруются с 0, а чтобы последний - к −1." ] }, { "cell_type": "code", "execution_count": 36, "id": "4aaa7d90", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'tuple', 'dict', 'set']\n", "list\n", "set\n" ] } ], "source": [ "print(collections)\n", "\n", "print(collections[0])\n", "print(collections[-1])" ] }, { "cell_type": "markdown", "id": "9314d073", "metadata": {}, "source": [ "Также индексы можно использовать для того, чтобы менять какие-то элементы внутри списка, например, мы можем заменить `set` на `frozenset`, обратившись к переменной по третьему индексу." ] }, { "cell_type": "code", "execution_count": 37, "id": "58c77b64", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'tuple', 'dict', 'frozenset']\n" ] } ], "source": [ "collections[3] = \"frozenset\"\n", "\n", "print(collections)" ] }, { "cell_type": "markdown", "id": "3c710948", "metadata": {}, "source": [ "Если мы обратимся по индексу, которого не существует, Python выдаст вам ошибку, скажет, что `list index out of range`, значит, что такого индекса еще нет. Опять же, ошибки в Python'е очень говорящие." ] }, { "cell_type": "code", "execution_count": 38, "id": "0310e5b8", "metadata": {}, "outputs": [ { "ename": "IndexError", "evalue": "list index out of range", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mIndexError\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[0mcollections\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mIndexError\u001b[0m: list index out of range" ] } ], "source": [ "collections[10]" ] }, { "cell_type": "markdown", "id": "e6986171", "metadata": {}, "source": [ "Читайте их, не бойтесь, они вам скажут, что именно произошло не так. Также можно проверить, существует ли конкретный объект в списке, с помощью оператора `in`. Например, у нас есть `tuple` в наших `collections`." ] }, { "cell_type": "code", "execution_count": 39, "id": "e7e3adfc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"tuple\" in collections" ] }, { "cell_type": "markdown", "id": "026bfc3a", "metadata": {}, "source": [ "Также списки поддерживают срезы так же, как и строки, работает все точно так же. Например, мы можем получить определенный набор элементов с помощью среза от первого до третьего." ] }, { "cell_type": "code", "execution_count": 40, "id": "e4cf1f58", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] } ], "source": [ "range_list = list(range(10))\n", "print(range_list)" ] }, { "cell_type": "code", "execution_count": 41, "id": "43e860e6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2]" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[1:3]" ] }, { "cell_type": "markdown", "id": "ab45e27a", "metadata": {}, "source": [ "Последний индекс не включается, будьте внимательны, и так как элементы в списках нумеруются с 0, у нас не включается 0 здесь. Мы можем опустить второй индекс и таким образом получить все элементы, начиная с третьего индекса, или, наоборот, до пятого индекса." ] }, { "cell_type": "code", "execution_count": 42, "id": "6673dc75", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3, 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[3:]" ] }, { "cell_type": "code", "execution_count": 43, "id": "66cf8cbb", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4]" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[:5]" ] }, { "cell_type": "markdown", "id": "a107e276", "metadata": {}, "source": [ "Также мы можем опустить оба, и начальный и конечный индекс, и, например, скакать через один, получая только четные элементы списка." ] }, { "cell_type": "code", "execution_count": 44, "id": "342bac0e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] } ], "source": [ "print(range_list)" ] }, { "cell_type": "code", "execution_count": 45, "id": "4b00d12e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 2, 4, 6, 8]" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[::2]" ] }, { "cell_type": "markdown", "id": "02cffa19", "metadata": {}, "source": [ "Мы можем также разворачивать список, обращаясь к −1 или делать более сложные операции, которые довольно редко встречаются, но важно представлять, что именно там происходит." ] }, { "cell_type": "code", "execution_count": 17, "id": "c3722991", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[::-1]" ] }, { "cell_type": "code", "execution_count": 18, "id": "ec0f88a5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 3, 5, 7, 9]" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[1::2]" ] }, { "cell_type": "code", "execution_count": 19, "id": "26023033", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[5, 4, 3, 2]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range_list[5:1:-1]" ] }, { "cell_type": "markdown", "id": "da4080c4", "metadata": {}, "source": [ "Также важно знать, что при получении среза у вас создается новый объект, то есть получается новый список в этот момент." ] }, { "cell_type": "code", "execution_count": 46, "id": "5d0d323f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(range_list)\n", "print(range_list[:])\n", "\n", "range_list[:] is range_list" ] }, { "cell_type": "markdown", "id": "df2e16eb", "metadata": {}, "source": [ "Списки, как и все коллекции, поддерживают протокол итерации, значит, мы можем использовать цикл `for` для того, чтобы итерироваться по элементам списка." ] }, { "cell_type": "markdown", "id": "5986024c", "metadata": {}, "source": [ "Обратите внимание, что итерации происходят именно по элементам списка, а не по индексам, как во многих языках.\n", "\n", "Например, мы можем итерироваться по нашим collections и выводить текущую коллекцию." ] }, { "cell_type": "code", "execution_count": 47, "id": "10d90a2a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Learning list...\n", "Learning tuple...\n", "Learning dict...\n", "Learning set...\n" ] } ], "source": [ "collections = [\"list\", \"tuple\", \"dict\", \"set\"]\n", "\n", "for collection in collections:\n", " print(f\"Learning {collection}...\")" ] }, { "cell_type": "markdown", "id": "0e1999ee", "metadata": {}, "source": [ "Часто бывает нужно выводить не только элемент списка, а также его индекс. Для этого существует встроенная функция `enumerate`, которая возвращает индекс и текущий элемент.\n", "\n", "Например, мы можем вывести `collection` и соответствующий индекс." ] }, { "cell_type": "code", "execution_count": 48, "id": "2b746578", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0. list\n", "1. tuple\n", "2. dict\n", "3. set\n" ] } ], "source": [ "for idx, collection in enumerate(collections):\n", " print(f\"{idx}. {collection}\")" ] }, { "cell_type": "markdown", "id": "41322654", "metadata": {}, "source": [ "Так как списки являются изменяемыми структурами данных, мы можем добавлять и удалять элементы.\n", "\n", "Для добавления элемента в список существует встроенный метод списка `append`, который добавляет переданную переменную в конец списка." ] }, { "cell_type": "code", "execution_count": 49, "id": "05b8d29e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'tuple', 'dict', 'set', 'OrderedDict']\n" ] } ], "source": [ "collections.append(\"OrderedDict\")\n", "\n", "print(collections)" ] }, { "cell_type": "markdown", "id": "f9420aed", "metadata": {}, "source": [ "Например, мы можем добавить в наши `collections` `OrderedDict`.\n", "\n", "Если вам нужно расширить список каким-то другим списком, вы можете использовать метод `extend`, который добавляет переданный список в конец вашего списка." ] }, { "cell_type": "code", "execution_count": 50, "id": "fbf75b4e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'tuple', 'dict', 'set', 'OrderedDict', 'ponyset', 'unicorndict']\n" ] } ], "source": [ "collections.extend([\"ponyset\", \"unicorndict\"])\n", "\n", "print(collections)" ] }, { "cell_type": "markdown", "id": "f0683229", "metadata": {}, "source": [ "Также можно использовать перегруженный оператор `+`, который делает логичную вещь, он добавляет переменную в конец, так же как `append` или `extend`." ] }, { "cell_type": "code", "execution_count": 51, "id": "bedcf18e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'tuple', 'dict', 'set', 'OrderedDict', 'ponyset', 'unicorndict', None]\n" ] } ], "source": [ "collections += [None]\n", "\n", "print(collections)" ] }, { "cell_type": "markdown", "id": "2059bc0b", "metadata": {}, "source": [ "Если вам нужно удалить какой-то элемент списка, можно использовать оператор `del` и указать индекс, который вы хотите удалить." ] }, { "cell_type": "code", "execution_count": 52, "id": "53e26d94", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['list', 'tuple', 'dict', 'set', 'ponyset', 'unicorndict', None]\n" ] } ], "source": [ "del collections[4]\n", "\n", "print(collections)" ] }, { "cell_type": "markdown", "id": "1b044d2f", "metadata": {}, "source": [ "Также существуют несколько полезных встроенных функций, которые помогут вам работать со списками, это `min`, `max` и `sum`. Они делают именно то, как называются, то есть, мы можем получить минимальный элемент, максимальный элемент или сумму элементов списка." ] }, { "cell_type": "code", "execution_count": 53, "id": "eaa5caa3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "19\n", "80\n" ] } ], "source": [ "numbers = [4, 17, 19, 9, 2, 6, 10, 13]\n", "\n", "print(min(numbers))\n", "print(max(numbers))\n", "print(sum(numbers))" ] }, { "cell_type": "markdown", "id": "1e6bf2e7", "metadata": {}, "source": [ "Еще один полезный метод, который работает со списками и строками, это метод строки `join`.\n", "Он позволяет взять список и форматировать строку с помощью разделителя какого-то." ] }, { "cell_type": "code", "execution_count": 55, "id": "6abea50a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "python, perl, pascal\n" ] } ], "source": [ "tag_list = [\"python\", \"perl\", \"pascal\"]\n", "print(\", \".join(tag_list))" ] }, { "cell_type": "markdown", "id": "3a8b4d4c", "metadata": {}, "source": [ "Еще одна часто встречающаяся операция со списками - это сортировка. В Python'е существует несколько методов сортировки, о которых мы сейчас с вами поговорим, а для начала создадим случайный список с помощью встроенного модуля `random`. Это модуль стандартной библиотеки, который вам не нужно дополнительно устанавливать. Создадим пустой список и в цикле добавим в него случайный элемент с помощью функции `append`, как вы видите." ] }, { "cell_type": "code", "execution_count": 56, "id": "5b839563", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[6, 14, 13, 16, 7, 4, 11, 9, 7, 16]\n" ] } ], "source": [ "import random\n", "\n", "numbers = []\n", "\n", "for _ in range(10):\n", " numbers.append(random.randint(1, 20))\n", " \n", "print(numbers)" ] }, { "cell_type": "markdown", "id": "d4f11a29", "metadata": {}, "source": [ "Обратите внимание, что в цикле `for` используется для итерации нижнее подчеркивание, эта переменная говорит о том, что нам не важно, что именно присваивается при итерации.\n", "Таким образом, нам важно, что происходит итерация 10 раз, 10 раз присваивается новый элемент, а переменная сама нам не важна.\n", "\n", "У нас получился какой-то список, давайте попробуем его отсортировать.\n", "Для сортировки в Python'е существует встроенная функция `sorted`, которая возвращает отсортированный список. Все очень просто и логично." ] }, { "cell_type": "code", "execution_count": 57, "id": "0dfdf09a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[4, 6, 7, 7, 9, 11, 13, 14, 16, 16]\n", "[6, 14, 13, 16, 7, 4, 11, 9, 7, 16]\n" ] } ], "source": [ "print(sorted(numbers))\n", "print(numbers)" ] }, { "cell_type": "markdown", "id": "5e8e66b9", "metadata": {}, "source": [ "Обратите внимание, что список возвращается именно новый, то есть старый список сохраняется неизменным. Если вам нужно изменить список, который вы имеете уже, можно использовать метод списка `sort`, который изменяет список `inplace`, сортирует его `inplace`." ] }, { "cell_type": "code", "execution_count": 58, "id": "b00c7345", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[4, 6, 7, 7, 9, 11, 13, 14, 16, 16]\n" ] } ], "source": [ "numbers.sort()\n", "print(numbers)" ] }, { "cell_type": "markdown", "id": "cd069246", "metadata": {}, "source": [ "Если вам нужно отсортировать список в обратном порядке, можно передать аргумент `reverse=True` в функцию sorted или в функцию sort метод списка." ] }, { "cell_type": "code", "execution_count": 60, "id": "e931b1dc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[16, 16, 14, 13, 11, 9, 7, 7, 6, 4]\n" ] } ], "source": [ "print(sorted(numbers, reverse=True))" ] }, { "cell_type": "code", "execution_count": 61, "id": "97f2d225", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[16, 16, 14, 13, 11, 9, 7, 7, 6, 4]\n" ] } ], "source": [ "numbers.sort(reverse=True)\n", "print(numbers)" ] }, { "cell_type": "markdown", "id": "b8dcc547", "metadata": {}, "source": [ "Также существует встроенная функция `reversed`, которая возвращает некий `reverse iterator`." ] }, { "cell_type": "code", "execution_count": 62, "id": "d0b2693d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(reversed(numbers))" ] }, { "cell_type": "markdown", "id": "6713e790", "metadata": {}, "source": [ "Мы об итераторах поговорим позднее, пока важно понимать, что это просто объект, который поддерживает протокол итерации, и можно его преобразовать в список, а получится, в принципе, то же самое.\n", "\n", "Кроме методов, которые мы обсудили в этой лекции, существует также много других, о которых вы можете почитать в документации, например, метод `index` или `insert`." ] }, { "cell_type": "markdown", "id": "14e838d3", "metadata": {}, "source": [ "Собственно, в документации все про них подробно написано, не бойтесь это смотреть." ] }, { "cell_type": "markdown", "id": "5e5ce574", "metadata": {}, "source": [ "А следующей структурой данных, о которой мы поговорим в этой лекции, являются кортежи.\n", "\n" ] }, { "cell_type": "markdown", "id": "b93e3034", "metadata": {}, "source": [ "Кортежи, по сути, это неизменяемые списки, то есть это какой-то упорядоченный набор объектов, который мы не можем изменить - мы не можем ни добавлять, ни удалять элементы из него. \n", "\n", "Кортежи определяются с помощью круглых скобочек или литерала `tuple`." ] }, { "cell_type": "code", "execution_count": 65, "id": "1caefad6", "metadata": {}, "outputs": [], "source": [ "empty_tuple = ()\n", "empty_tuple = tuple()" ] }, { "cell_type": "markdown", "id": "8e935365", "metadata": {}, "source": [ "Например, мы можем создать кортеж `immutables`, куда положим неизменяемые типы наши." ] }, { "cell_type": "code", "execution_count": 64, "id": "b431f577", "metadata": {}, "outputs": [], "source": [ "immutables = (int, str, tuple)" ] }, { "cell_type": "markdown", "id": "de831021", "metadata": {}, "source": [ "Если мы попробуем в качестве нулевого элемента сделать `float`, у нас выведется ошибка, потому что кортежи неизменяемы." ] }, { "cell_type": "code", "execution_count": 66, "id": "3f059a2f", "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "'tuple' object does not support item assignment", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mTypeError\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[0mimmutables\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfloat\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" ] } ], "source": [ "immutables[0] = float" ] }, { "cell_type": "markdown", "id": "b0b7177b", "metadata": {}, "source": [ "Однако будьте внимательны, несмотря на то что сами кортежи неизменяемые, объекты внутри них могут быть изменяемыми.\n", "\n", "Например, если у нас кортеж содержит список, мы можем добавлять элементы в этот список. Например, мы можем присоединить нолик." ] }, { "cell_type": "code", "execution_count": 68, "id": "7c5acc0a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "([0], [])\n" ] } ], "source": [ "blink = ([], [])\n", "blink[0].append(0)\n", "\n", "print(blink)" ] }, { "cell_type": "markdown", "id": "f5f5d867", "metadata": {}, "source": [ "Также важная особенность кортежей — у них есть функция `hash`, к ним применяется функция `hash`, и поэтому они могут использоваться в качестве ключей в словарях, о которых мы поговорим чуть позднее." ] }, { "cell_type": "code", "execution_count": 69, "id": "1e0e50e1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "750394483" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hash(tuple())" ] }, { "cell_type": "markdown", "id": "eaaab34b", "metadata": {}, "source": [ "Будьте внимательней при определении кортежа из одного элемента, не забывайте писать запятую, потому что если вы забудете про нее, то Python сочтет вашу переменную типом `int`." ] }, { "cell_type": "code", "execution_count": 70, "id": "ebb0c4e9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "one_element_tuple = (1,)\n", "guess_what = (1)\n", "\n", "type(guess_what)" ] }, { "cell_type": "markdown", "id": "1a57f366", "metadata": {}, "source": [ "Итак, на этой лекции мы с вами поговорили про списки.\n", "\n", "Списки — это упорядоченный и изменяемый набор объектов. Они поддерживают итерацию, вы можете применять к ним срезы или обращаться к элементам по индексам. Также в них довольно много встроенных методов и каких-то функций, которые вы можете использовать вместе со списками.\n", "\n", "Также мы разобрали кортежи.\n", "Кортежи — это неизменяемая версия списков, и кортежи могут использоваться в качестве ключей в словарях.\n", "\n", "На следующей лекции мы посмотрим с вами на задачу на изученный материал." ] } ], "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 }