{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Основы программирования в Python\n", "\n", "*Алла Тамбовцева, НИУ ВШЭ*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Списки в Python и функция `range()`\n", "\n", "Как выглядит список, вы уже выяснили, прослушав главу на DataCamp. Список – это структура данных, которая может включать в себя элементы разных типов:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[5, 6, 8, 1]" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nums = [5, 6, 8, 1]\n", "nums" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Anna', 'Fred', 'Tina']" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names = [\"Anna\", \"Fred\", \"Tina\"]\n", "names" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Anna', 10, 'Fred', 7]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mixed = [\"Anna\", 10, \"Fred\", 7]\n", "mixed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Изменяемость и неизменяемость + метод `.append()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Важная особенность списков заключается в том, что они являются изменяемой (*mutable*) структурой данных. Это означает, что список можно изменять, не переопределяя переменную, в которой этот список сохранен. Чтобы понять, о чём речь, давайте посмотрим сначала на примеры неизменяемых (*immutable*) объектов в Python. Начнём с целых чисел. Если у нас есть переменная `a`, и в ней сохранено целое число, мы сможем изменить её значение только создав новую переменную с таким же названием:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 3\n", "a = a + 1 # новое значение\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Другими словами, у нас нет никакого иного способа, который можно было бы применить, чтобы, например, увеличить значение `a`, не переопределяя её через оператор `=`. То же будет и со строками. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "s = \"питон греется на солнышке\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь попробуем сделать первую букву заглавной:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Питон греется на солнышке'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.capitalize() " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Но сама строка пока не изменилась – всё потому, что строка является объектом неизменяемого типа:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'питон греется на солнышке'" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Чтобы сохранить изменения, нужно переопределить переменную `s`:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Питон греется на солнышке'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = s.capitalize()\n", "s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "К неизменяемым объектам в Python относятся:\n", " \n", "* целые числа (*int*);\n", "* числа с плавающей точкой (*float*);\n", "* строки (*str*);\n", "* логические значения (*bool*);\n", "* кортежи (*tuples*).\n", "\n", "Теперь посмотрим на изменяемые объекты. К изменяемым объектам относятся:\n", "\n", "* списки (*list*);\n", "* словари (*dictionary*);\n", "* множества (*set*)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Есть список `M`:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "M = [1, 9, 4] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Заменим первый элемент на 100:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[100, 9, 4]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[0] = 100\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Изменения сохранились, но при этом значение переменной `M` не пришлось переопределять заново. Теперь припишем в конец списка `M` ещё один элемент – для это применим метод `.append()` :" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[100, 9, 4, 200]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.append(200)\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Список `M` изменился, но присваивание с `=` мы нигде не использовали, мы изменили список «как есть». Более того, если мы исполним что-то такое `M = M.append(200)`, мы получим неожиданный результат:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None\n" ] } ], "source": [ "M = M.append(200)\n", "print(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Результат `None`. Почему? Потому что метод `.append()` сам по себе не возвращает никакого результата (результат пуст, имеет тип `None`), он «молча» записывает элемент в конец списка. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "При работе с изменяемыми объектами нужно быть осторожными при создании копии. Если мы работаем с неизменяемыми объектами, можно просто присвоить новой переменной значение старой с помощью `=`:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3 4\n" ] } ], "source": [ "a = 3\n", "b = a\n", "b = b + 1\n", "print(a, b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В примере выше мы скопировали значение `a` в переменную `b`, значение `b` изменили, а значение `a` изменений не претерпело." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "С изменяемыми объектами это бы не сработало. Вернемся к спискам." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 6, 7, 10] [1, 6, 7, 10]\n" ] } ], "source": [ "L = [1, 6, 7]\n", "L2 = L\n", "L2.append(10)\n", "print(L, L2) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Несмотря на то, что список `L` мы не трогали, он изменился точно так же, как и список `L2`! Что произошло? На самом деле, когда мы записали `L2 = L`, мы скопировали не сам список, а ссылку на него. Другими словами, проводя аналогию с папкой и ярлыком, вместо того, чтобы создать новую папку `L2` с элементами, такими же, как в `L`, мы создали ярлык `L2`, который сам по себе ничего не содержит, а просто ссылается на папку `L`.\n", "\n", "Так как же тогда копировать списки? Во-первых, у списков есть метод `.copy()`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 6, 7] [1, 6, 7, 10]\n" ] } ], "source": [ "L = [1, 6, 7]\n", "L2 = L.copy() \n", "L2.append(10)\n", "print(L, L2) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Во-вторых, можно сделать полный срез срез и «срезать» весь список:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 6, 7] [1, 6, 7, 10]\n" ] } ], "source": [ "L = [1, 6, 7]\n", "L2 = L[:]\n", "L2.append(10)\n", "print(L, L2) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Метод `.extend()` vs метод `.append()`\n", "\n", "Метод `.append()` используется, если в конец списка нужно приписать один элемент. А что, если нужно добавить сразу несколько элементов? Метод `.append()` уже не подойдёт:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "append() takes exactly one argument (2 given)", "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[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m8\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[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: append() takes exactly one argument (2 given)" ] } ], "source": [ "a = [7, 2, 1, 8]\n", "a.append(3, 4) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А если объединить элементы в список, метод `.append()` сработает, но результат будет отличаться от ожидаемого:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[7, 2, 1, 8, [3, 4]]" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.append([3, 4]) # список [3, 4] как отдельный элемент\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Поэтому в таком случае нужно использовать особый метод, метод `.extend()`:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[7, 2, 1, 8, 3, 4]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = [7, 2, 1, 8]\n", "a.extend([3, 4]) \n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Результат выше совпадает с тем, что мы бы получили, «склеивая» два списка через `+`:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[7, 2, 1, 8, 3, 4]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = [7, 2, 1, 8]\n", "a + [3, 4] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Функция `range()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В Python есть функция `range()`, которая позволяет перебирать целые числа на заданном промежутке, не создавая при этом сам список чисел." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n" ] } ], "source": [ "# пример\n", "\n", "for j in range(0, 6):\n", " print(j)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Правый конец заданного в `range()` промежутка **не включается**, будьте бдительны. В примере выше на экран были выведены числа от 0 до 5, число 6 включено не было." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если мы хотим посмотреть на то, какие значения будут в `range()`, придется превратить его в список:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "range(0, 3)" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "range(0, 3) # не поспоришь, но бесполезно" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(range(0, 3)) # значения внутри range" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Полезный факт: если нас интересуют числа на промежутке, начиная с нуля, в `range()` левый конец можно не указывать, 0 будет выбран по умолчанию." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4]" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(range(5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Еще один полезный факт: шаг (разность между последующими элементами) для последовательности целых чисел можно выставить самостоятельно. Для этого достаточно дописать третье число – значение шага – внутри функции `range()`:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 2, 4, 6, 8, 10, 12, 14, 16]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(range(0, 18, 2)) # шаг 2" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 3, 6, 9, 12, 15]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(range(0, 18, 3)) # шаг 3" ] } ], "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.6.8" } }, "nbformat": 4, "nbformat_minor": 2 }