{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "#### Кафедра дискретной математики МФТИ\n", "\n", "Никита Волков\n", "\n", "На основе http://www.inp.nsk.su/~grozin/python/\n", "\n", "# Python\n", "\n", "При программировании на питоне первые полезные результаты\n", "начинают получаться минут через 5 после начала работы над\n", "проектом. Не надо изобретать никаких велосипедов: всё, что\n", "может понадобиться, доступно или в стандартной библиотеке,\n", "или в одном из многочисленных дополнительных пакетов.\n", "Программы получаются элегантные и лаконичные, их легко и\n", "приятно писать и читать. Имеется широкий выбор высококачественных инструментов программиста: интегрированные среды разработки, отладчики и средства тестирования и т.д. Питон прекрасно приспособлен для написания больших и сложных программных комплексов. И всё это совершенно не зависит от железа и операционной системы (конечно, если не вызывать низкоуровневых системно-зависимыех библиотек).\n", "\n", "Главный минус - питон, мягко говоря, не славится потрясающим быстродействием. Писать программы сложных вычислений на питоне, конечно, глупо. Но есть ряд способов обойти это ограничение. Если программа занимается регулярной обработкой больших массивов чисел с плавающей точкой, то использование numpy радикально повышает её быстродействие. Пакет numpy и его расширение scipy фактически делают matlab ненужным - дают ту же функциональность с более приятным языком программирование. Есть ряд пакетов для построения высококачественных графиков, например, matplotlib. Другое средство повышения быстродействия - cython. Пишется программа, выглядящая почти как питонская, но со вставленными статичестими декларациями типов. cython транслирует её в исходный код на C, что часто даёт быстродействие, сравнимое с вручную написанным C. Программа на cython-е может свободно вызывать функции из библиотек на C;\n", "её можно использовать из обычного питона. Ну и наконец можно написать критически важные для быстродействия системы в целом вычисления на другом языке (C например), и вызывать эти внешние программы из питона. Питон при этом выполняет роль клея - реализует логику высокого уровня, системно-независимый GUI и т.д.\n", "\n", "Питон - сильно типизированный язык с динамической типизацией. Всё, с чем работает программа - объекты; каждый из них принадлежит определённому типу. Если программа пытается выполнить какую-то операцию над объектом такого типа, который не поддерживает эту операцию, произойдёт ошибка времени выполнения. Описаний переменных в питоне нет. Одна и та же переменная может в разные моменты иметь значения - объекты разных типов. Ошибка в типах может проявиться много времени спустя после того, как программа сдана в эксплуатацию, особенно если она происходит в редко используемом участке кода.\n", "Язык допускает ситуацию, когда мы сначала присвоим какой-то переменной целое число, потом строку, потом ещё что-то, но это плохой стиль. Переменная заводится для одного конкретного использования, и естественно, чтобы её значения в любой момент подходили для этого использования и имели одинаковый тип. В исходный текст на питоне 3.5 можно включить (необязательные) аннотации типов переменных и функций, и прогнать её через программу статической проверки типов.\n", "\n", "# Питонячий стиль кода\n", "\n", "**Обязательно** прочитайте инструкцию по стилю кода: http://pep8.ru/doc/pep8/\n", "\n", "Желательно соблюдать стиль кода.\n", "Однако, поскольку у нас не курс программирование, то полное соблюдение стиля не требуется.\n", "Перечислим кратко основные правила:\n", "\n", "* Отступы составляют ровно 4 пробела, табуляции не используются. К слову, в Питоне нет ключевых слов по типу `{` и `}` в C и `begin` и `end` в Паскале. Блоки кода разделяются пробелами.\n", "* Все переменные должны иметь понятные названия и состоять только из строчных букв. Например, вместо того, чтобы назвать выборку как `X`, лучше назвать ее `sample`. В качестве разделителей используйте подчеркивания. В редких случаях можно и отступать от этого правила, если обозначения понятны из решаемой задачи.\n", "* Вокруг всех знаков арифметических операций, присваивания и пр. обязательны пробелы с двух сторон. Исключение - запись вида `a=b` в аргументах функции. Примеры будут далее.\n", "* Разделяйте логические куски программы пустыми строками. Врядли вы сможете написать код строк на 10-15, в который нельзя вставить ни одну пустую строку, разделяющую код на логические части. ПЕРЕД всеми логическими кусками кода должен быть комментарий к этому куску кода.\n", "* Все функции (кроме самых маленьких) должны содержать подробную документацию, написанную по правилам оформления документаций.\n", "* Если комментарий дается на строке с кодом, то оформить его стоит так:\n", "\n", "`код [ровно 2 пробела] # [ровно 1 пробел] комментарий`\n", "\n", "* Если комментарий дается на отдельной строке, то он должен иметь тот же отступ, что и строка кода под ним. Скорее всего перед таким комментарием можно вставить пустую строку.\n", "* Не нужно комментировать очевидное.\n", "* Не нужно писать весь код в одной ячейке ноутбука!!!\n", "* Если есть возможность, при выводе десятичных чисел следует оставлять разумное число знаков после запятой.\n", "\n", "Отдельно стоит отметить, что код должен быть понятен проверяющему.\n", "В частности, должны использоваться понятные названия переменных и присутствовать подробные комментарии.\n", "**Если проверяющий не поймет код, оценка за задание может быть снижена**, и будет пичалька.\n", "\n", "# Числа\n", "\n", "Арифметические операции имеют ожидаемые приоритеты. При необходимости используются скобки." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 + 2 * 3" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1 + 2) * 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Возведение целого числа в целую степень даёт целое число, если показатель степени $\\ge0$, и число с плавающей точкой, если он $<0$. Так что тип результата невозможно определить статически, если значение переменной `n` неизвестно." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = 3\n", "2 ** n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.125" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = -3\n", "2 ** n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Арифметические операции можно применять к целым и числам с плавающей точкой в любых сочетаниях." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-2.0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n + 1.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Деление целых чисел всегда даёт результат с плавающей точкой, даже если они делятся нацело. Операторы `//` и `%` дают целое частное и остаток." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.75" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 / 4" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 // 4" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 % 4" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.0" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 / 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если Вы попытаетесь использовать переменную, которой не присвоено никакого значения, то получите сообщение об ошибке." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'x' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\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[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'x' is not defined" ] } ], "source": [ "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`x+=1` означает `x=x+1`, аналогично для других операций. В питоне строго различаются операторы (например, присваивание) и выражения, так что таких операций, как `++` в C, нет. Хотя вызов функции в выражении может приводить к побочным эффектам." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "x = 1\n", "x += 1\n", "print(x)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n" ] } ], "source": [ "x *= 2\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Удаление объекта" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'x' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\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[0;32mdel\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'x' is not defined" ] } ], "source": [ "del x\n", "x" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "x = 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Любопытная особенность питона: можно использовать привычные из математики сравнения вроде $x\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'123x'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: '123x'" ] } ], "source": [ "int('123x')" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.23456e-05" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = float('123.456E-7')\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метод `format` особенно полезен для вывода." ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'str: 123 int: 123 float: 1.23456e-05'" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'str: {} int: {} float: {}'.format(s, n, x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ширина поля 5 (больше, если не влезет); десятичный, шестнадцатиричный и двоичный форматы." ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 123\n", " 7b\n", "1111011\n" ] } ], "source": [ "print('''{:5d}\n", "{:5x}\n", "{:5b}'''.format(n, n, n))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ширина поля 10 (больше, если не влезет); после десятичной точки 5 цифр; формат с фиксированной точкой или экспоненциальный." ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.00001\n", "1.23456e-05\n", "81000.51840\n", "8.10005e+04\n" ] } ], "source": [ "print('''{:10.5f}\n", "{:10.5e}\n", "{:10.5f}\n", "{:10.5e}'''.format(x, x, 1 / x, 1 / x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Списки\n", "\n", "Списки могут содержать объекты любых типов (в одном списке могут быть объекты разных типов). Списки индексируются так же, как строки." ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", "l" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(l)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[0]" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3]" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "ename": "IndexError", "evalue": "list index out of range", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\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[0ml\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: list index out of range" ] } ], "source": [ "l[10]" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[-1]" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[-2]" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2]" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[1:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Обратите внимание, что `l[:3]+l[3:]==l`." ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2]" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[:3]" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3, 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3:]" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3:3]" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3, 4, 5, 6, 7]" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3:-2]" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4, 5, 6, 7]" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[:-2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Списки являются изменяемыми объектами. Это сделано для эффективности. В списке может быть 1000000 элементов. Создавать его копию каждый раз, когда мы изменили один элемент, слишком дорого." ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 'три', 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3] = 'три'\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно заменить какой-нибудь подсписок на новый список (в том числе другой длины)." ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 0, 'три', 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3:3] = [0]\n", "l" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 10, 11, 12, 0, 'три', 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3:3] = [10, 11, 12]\n", "l" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 10, 11, 0, 0, 0, 0, 'три', 4, 5, 6, 7, 8, 9]" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[5:7] = [0, 0, 0, 0]\n", "l" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2]" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3:] = []\n", "l" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4]" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[len(l):] = [3, 4]\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Некоторые из этих операций могут быть записаны в другой форме." ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 4, 5, 6, 7]" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [0, 1, 2, 3, 4, 5, 6, 7]\n", "del l[3]\n", "l" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 6, 7]" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "del l[3:5]\n", "l" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 0, 6, 7]" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l.insert(3, 0)\n", "l" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 0, 6, 7, 8]" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l.append(8)\n", "l" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 0, 6, 7, 8, 9, 10, 11]" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l.extend([9, 10, 11])\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Элементы списка могут быть разных типов." ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, [1, 'x', 3], 'abc']" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [0, [1, 2, 3], 'abc']\n", "l[1][1] = 'x'\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Когда мы пишем `m=l`, мы присваиваем переменной `m` ссылку на тот же объект, на который ссылается `l`. Поэтому, изменив этот объект (список) через `l`, мы увидим эти изменения и через `m` - ведь список всего один." ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 'три', 4, 5]" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [0, 1, 2, 3, 4, 5]\n", "m = l\n", "l[3] = 'три'\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Операция `is` проверяет, являются ли `m` и `l` **одним и тем же объектом**." ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m is l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если мы хотим видоизменять `m` и `l` независимо, нужно присвоить переменной `m` не список `l`, а его копию. Тогда это будут два различных списка, просто в начальный момент они состоят из одних и тех же элементов. Для этого в питоне есть идиома: `l[:]` - это подсписок списка `l` от начала до конца, а подсписок всегда копируется." ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "collapsed": true }, "outputs": [], "source": [ "m = l[:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь `m` и `l` - два независимых объекта, имеющих равные значения." ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m is l" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m == l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Их можно менять независимо." ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 0, 4, 5]" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l[3] = 0\n", "l" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 'три', 4, 5]" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как и для строк, сложение списков означает конкатенацию, а умножение на целое число - повторение списка несколько раз. Операция `in` проверяет, содержится ли элемент в списке." ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4, 5]" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[0, 1, 2] + [3, 4, 5]" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 0, 1, 2]" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 * [0, 1, 2]" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4, 5]" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [0, 1, 2]\n", "l += [3, 4, 5]\n", "l" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 in l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Простейший вид цикла в питоне - это цикл по элементам списка." ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n" ] } ], "source": [ "for x in l:\n", " print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно использовать цикл `while`. В этом примере он выполняется, пока список `l` не пуст. Этот цикл гораздо менее эффективен, чем предыдущий - в нём на каждом шаге меняется список `l`. Он тут приведён не для того, чтобы ему подражали, а просто чтобы показать синтаксис цикла `while`." ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n" ] } ], "source": [ "while l:\n", " print(l[0])\n", " l = l[1:]" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Очень часто используются циклы по диапазонам целых чисел." ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n" ] } ], "source": [ "for i in range(4):\n", " print(i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `range(n)` возвращает диапазон целых чисел от 0 до $n-1$ (всего $n$ штук) в виде специального объекта `range`, который можно использовать в `for` цикле. Можно превратить этот объект в список функцией `list`. Но этого делать не нужно, если только такой список не нужен для проведения каких-нибудь списковых операций. Число `n` может быть равно 1000000. Зачем занимать память под длинный список, если он не нужен? Для написания цикла достаточно короткого объекта `range`, который хранит только пределы." ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "range(0, 4)" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = range(4)\n", "r" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3]" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функции `range` можно передать первый параметр - нижний предел." ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "3\n" ] } ], "source": [ "for i in range(2, 4):\n", " print(i)" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "range(2, 4)" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = range(2, 4)\n", "r" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2, 3]" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно также передать шаг третьим аргументом" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "3\n", "5\n", "7\n", "9\n" ] } ], "source": [ "for i in range(1, 10, 2):\n", " print(i)" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "range(1, 10, 2)" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = range(1, 10, 2)\n", "r" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 3, 5, 7, 9]" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `list` превращает строку в список символов." ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['а', 'б', 'в', 'г', 'д']" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = list('абвгд')\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как написать цикл, если в его теле нужно использовать и номера элементов списка, и сами эти элементы? Первая идея, которая приходит в голову по аналогии с C - это использовать `range`." ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 а\n", "1 б\n", "2 в\n", "3 г\n", "4 д\n" ] } ], "source": [ "for i in range(len(l)):\n", " print(i, ' ', l[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно поступить наоборот - устроить цикл по элементам списка, а индексы вычислять." ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 а\n", "1 б\n", "2 в\n", "3 г\n", "4 д\n" ] } ], "source": [ "i = 0\n", "for x in l:\n", " print(i, ' ', x)\n", " i += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Оба этих способа не есть идиоматический питон. Более изящно использовать функцию `enumerate`, которая на каждом шаге возвращает пару из индекса `i` и `i`-го элемента списка." ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 а\n", "1 б\n", "2 в\n", "3 г\n", "4 д\n" ] } ], "source": [ "for i, x in enumerate(l):\n", " print(i, ' ', x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Про такие пары мы поговорим в следующем параграфе.\n", "\n", "Довольно часто удобно использовать цикл `while True:`, то есть пока рак на горе не свистнет, а выход (или несколько выходов) из него устраивать в нужном месте (или местах) при помощи `break`." ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "д\n", "г\n", "в\n", "б\n", "а\n" ] } ], "source": [ "while True:\n", " print(l[-1])\n", " l = l[:-1]\n", " if l == []:\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Этот конкретный цикл - отнюдь не пример для подражания, он просто показывает синтаксис.\n", "\n", "Можно строить список поэлементно." ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = []\n", "for i in range(10):\n", " l.append(i ** 2)\n", "l" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Но более компактно и элегантно такой список можно создать при помощи генератора списка (list comprehension). К тому же это эффективнее - размер списка известен заранее, и не нужно много раз увеличивать его. Опытные питон-программисты используют генераторы списков везде, где это возможно (и разумно)." ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[i ** 2 for i in range(10)]" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[[i, j] for i in range(3) for j in range(2)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В генераторе списков могут присутствовать некоторые дополнительные элементы, хотя они используются реже. Например, в список-результат можно включить не все элементы." ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 36, 49, 64, 81]" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[i ** 2 for i in range(10) if i != 5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создадим список случайных целых чисел." ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from random import randint" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6, 1, 1, 1, 7, 3, 7, 7, 2, 0]" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [randint(0, 9) for i in range(10)]\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `sorted` возвращает отсортированную копию списка. Метод `sort` сортирует список на месте. Им можно передать дополнительный параметр - функцию, указывающую, как сравнивать элементы." ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 1, 1, 2, 3, 6, 7, 7, 7]" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sorted(l)" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6, 1, 1, 1, 7, 3, 7, 7, 2, 0]" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 1, 1, 2, 3, 6, 7, 7, 7]" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l.sort()\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Аналогично, функция `reversed` возвращает обращённый список (точнее говоря, некий объект, который можно использовать в `for` цикле или превратить в список функцией `list`). Метод `reverse` обращает список на месте." ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[7, 7, 7, 6, 3, 2, 1, 1, 1, 0]" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(reversed(l))" ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 1, 1, 2, 3, 6, 7, 7, 7]" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[7, 7, 7, 6, 3, 2, 1, 1, 1, 0]" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l.reverse()\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метод `split` расщепляет строку в список подстрок. По умолчанию расщепление производится по пустым промежуткам (последовательностям пробелов и символов `tab` и `newline`). Но можно передать ему дополнительный аргумент - подстроку-разделитель." ] }, { "cell_type": "code", "execution_count": 145, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['abc', 'def', 'ghi']" ] }, "execution_count": 145, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = 'abc \\t def \\n ghi'\n", "l = s.split()\n", "l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Чтобы напечатать элементы списка через запятую или какой-нибудь другой символ (или строку), очень полезен метод `join`. Он создаёт строку из всех элементов списка, разделяя их строкой-разделителем. Запрограммировать это в виде цикла было бы существенно длиннее, и такую программу было бы сложнее читать." ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'abc, def, ghi'" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s = ', '.join(l)\n", "s" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['abc', 'def', 'ghi']" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.split(', ')" ] } ], "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.3" } }, "nbformat": 4, "nbformat_minor": 1 }