{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Программирование на языке Python для сбора и анализа данных\n", "\n", "*Текст лекции: Щуров И.В., НИУ ВШЭ*\n", "\n", "Данный notebook является конспектом лекции по курсу «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ, 2015-16). Он распространяется на условиях лицензии [Creative Commons Attribution-Share Alike 4.0](http://creativecommons.org/licenses/by-sa/4.0/). При использовании обязательно упоминание автора курса и аффилиации. При наличии технической возможности необходимо также указать активную гиперссылку на [страницу курса](http://math-info.hse.ru/s15/m). Фрагменты кода, включенные в этот notebook, публикуются как [общественное достояние](http://creativecommons.org/publicdomain/zero/1.0/).\n", "\n", "Другие материалы курса, включая конспекты и видеозаписи лекций, а также наборы задач, можно найти на [странице курса](http://math-info.hse.ru/s15/m)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Лекция №3: Ввод-вывод списков и проверка условий" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ввод-вывод списков\n", "Для решения домашних заданий, да и вообще в жизни, нам нужно уметь преобразовывать списки в строки и наоборот. Рассмотрим несколько примеров.\n", "#### Превращение строки в список\n", "Возьмём строку, состояющую из слов, разделённых пробелами (может быть не одним) и символами конца строки." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "collapsed": true }, "outputs": [], "source": [ "s = \"hello world this is a\\ntest\"\n", "print(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если мы хотим работать с отдельными словами, входящими в эту строку, то её нужно разделить на отдельные слова — то есть получить список, состоящий из слов, которые входят в эту строку. Для этого можно использовать метод `split()`." ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "['hello', 'world', 'this', 'is', 'a', 'test']" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.split()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Итак, мы получили ровно то, что хотели. Заметим, что в качестве разделителя в этом случае использовались пробелы (один или несколько), а также любые *пробельные символы*, в число которых входит символ табуляции (у нас его не было) и символ перевода строки `\\n`. Разделители в элементы получающегося списка не попадают.\n", "\n", "**Важно!** Метод `split()` не меняет строку (вообще говоря строку в принципе нельзя поменять):" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello world this is a\n", "test\n" ] } ], "source": [ "s = \"hello world this is a\\ntest\"\n", "s.split()\n", "print(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В коде выше вторая строка не приводит ни к какому эффекту — вы создали список и мгновенно его забыли, а строка осталась неизменной. Если вы хотите что-то дальше делать со списком, полученным в результате применения `split()`, а не просто на него полюбоваться и забыть навсегда, то нужно, например, сохранить его в виде какой-то переменной:" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello\n", "world\n", "this\n", "is\n", "a\n", "test\n" ] } ], "source": [ "words = s.split()\n", "# теперь результат выполнения s.split() сохранён в переменную words\n", "for word in words:\n", " print(word)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "У метода `split()` есть необязательный параметр — разделитель. Если он указан, то строчка разбивается по тому разделителю, который ему передан." ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "['hello world', 'this is', 'a\\ntest']" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.split(\" \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Мы передали в качестве разделителя четыре пробела и теперь строка разделяется по ним, а один пробел или символ перевода строки не воспринимается как разделитель.\n", "\n", "Допустим, мы хотим ввести несколько чисел через запятую и вывести каждое из них, увеличенное на 1. Можно попробовать такой код:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите несколько чисел, разделённых запятыми: 2, 5, 10\n" ] }, { "ename": "TypeError", "evalue": "Can't convert 'int' object to str implicitly", "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 2\u001b[0m \u001b[0melements\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\",\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\u001b[0m \u001b[0melements\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: Can't convert 'int' object to str implicitly" ] } ], "source": [ "s = input(\"Введите несколько чисел, разделённых запятыми: \")\n", "elements = s.split(\",\")\n", "for n in elements:\n", " print(n+1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как нетрудно догадаться, он не работает: после разделения строки получается список, состоящий из строк. Чтобы превратить эти строки в числа, нужно использовать `int`. Вообще говоря, есть методы, которые позволяют довольно просто превратить все элементы списка в числа, но пока мы до них не дошли, будем обрабатывать в цикле каждый элемент в отдельности." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите несколько чисел, разделённых запятыми: 2, 3, 99\n", "3\n", "4\n", "100\n" ] } ], "source": [ "s = input(\"Введите несколько чисел, разделённых запятыми: \")\n", "elements = s.split(\",\")\n", "for n in elements:\n", " n = int(n)\n", " print(n+1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь работает!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Вывод списка в строчку" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Решим теперь обратную задачу: есть список, мы хотим его вывести в каком-то виде. Есть разные способы это сделать. Например, можно просто распечатать:" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['one', 'two', 'three']\n" ] } ], "source": [ "elements = [\"one\", \"two\", \"three\"]\n", "print(elements)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Тут элементы списка заключатся в скобки, а каждая строчка дополнительно ещё и в кавычки. Можно распечатать поэлементно в цикле, как в примере выше:" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "one\n", "two\n", "three\n" ] } ], "source": [ "for el in elements:\n", " print(el)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Так он выводится в столбик, потому что `print()` по умолчанию добавляет символ перевода строки `\\n` в конце каждой выводимой строчки. Если мы хотим вывести элементы списка как-то иначе, например, в строчку, но без скобок, запятых и кавычек, то нужно использовать другие способы.\n", "\n", "Первым из них является применение метода `join()`. Это метод строки, который позволяет склеить элементы списка с помощью какоего-то «клея». В некотором роде, это такой «`split()` наоборот»." ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "one two three\n" ] } ], "source": [ "print(\" \".join(elements))" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "one:two:three\n" ] } ], "source": [ "print(\":\".join(elements))" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "one----two----three\n" ] } ], "source": [ "print(\"----\".join(elements))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Это удобный метод, но у него есть ограничение: он требует, чтобы список, который ему подали на вход, состоял из строк. Если среди его элементов есть другие объекты, он ломается." ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "collapsed": false }, "outputs": [ { "ename": "TypeError", "evalue": "sequence item 0: expected str instance, int found", "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[0mnumbers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m9\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[0;32m----> 2\u001b[0;31m \u001b[0;34m\", \"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: sequence item 0: expected str instance, int found" ] } ], "source": [ "numbers = [6, 9, 10]\n", "\", \".join(numbers)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Эту проблему можно обойти (мы позже будем говорить про `map()` и списочные включения (list comprehensions)), но сейчас обсудим другой подход к выводу списков, основнный на функции `print()`.\n", "\n", "Напомним, что если передать функции `print` список, то он его распечатает в квадратных скобках через запятую." ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[6, 9, 10]\n" ] } ], "source": [ "print(numbers)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А если передать отдельные элементы списка, то он их распечатает без скобок и разделяя пробелом (вообще говоря, любым символом — это настраивается с помощью параметра `sep`)." ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6 9 10\n" ] } ], "source": [ "print(numbers[0], numbers[1], numbers[2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Строчка выше даёт тот результат, который мы хотели, но она будет работать только в том случае, когда в списке ровно три элемента: если элементов больше, выведены будут только три, если меньше, то возникнет ошибка. К счастью, в Python есть конструкция, которая позволяет «раздеть» список — передать все его элементы какой-то функции через запятую. Это делается с помощью звёздочки (`*`)." ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6 9 10\n" ] } ], "source": [ "print(*numbers)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Звёздочка как бы убирает квадратные скобки вокруг элементов списка. Сравните:" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5 8 9 11 Hello\n" ] } ], "source": [ "print(*[5, 8, 9, 11, \"Hello\"])" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5 8 9 11 Hello\n" ] } ], "source": [ "print(5, 8, 9, 11, \"Hello\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если вы хотите использовать не пробел, а какой-нибудь разделитель, то это тоже возможно:" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6, 9, 10\n" ] } ], "source": [ "print(*numbers, sep = ', ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Строки и срезы\n", "Строки могут вести себя почти как списки. Например, можно обращаться к отдельным элементам строки (к отдельным символам) или делать срезы." ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "collapsed": true }, "outputs": [], "source": [ "s = \"This is a test\"" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'s'" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s[6]" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'i'" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s[5]" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'s is '" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s[3:8]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Позже мы ещё поговорим про строки (это отдельная большая история)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Алгоритмы с циклами" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим пример алгоритма, в котором используется цикл. \n", "\n", "Напомним, как в прошлый раз мы искали числа Фибоначчи.\n", "\n", "Сначала мы выполняем *инициализацию* — устанавливаем начальные значения переменных, которые нам понадобятся в дальнейшем. Исходно мы знаем значения первых двух чисел Фибоначчи (это единицы); мы запишем их в переменные $a$ и $b$ и в дальнейшем будем хранить очередные два найденных числа Фибоначчи, необходимые для нахождения следующего числа." ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# инициализация (выполняется только один раз в начале алгоритма)\n", "a = 1 # первое число\n", "b = 1 # второе число" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Затем мы пишем код, осуществляющий переход к следующему числу. Мы будем выполнять его несколько раз, каждый раз получая новое число Фибоначчи." ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "c = a + b #нашли следующее число\n", "a = b # значение a нам уже не нужно, а вот значение b ещё пригодится\n", "b = c # запомнили вычисленное значение\n", "print(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выполнив эту ячейку несколько раз, мы будем получать последовательные числа Фибоначчи.\n", "\n", "Этот подход работает довольно неплохо, но если бы нам нужно было найти 115-е число Фибоначчи, мы бы замучались перезапускать ячейку. Вместо этого используем цикл `for`, который будет автоматически выполнять фрагмент кода, вычисляющий следующее число, столько раз, сколько нам нужно. Например, 10:" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 2\n", "1 3\n", "2 5\n", "3 8\n", "4 13\n", "5 21\n", "6 34\n", "7 55\n", "8 89\n", "9 144\n" ] } ], "source": [ "# инициализация (выполняется только один раз в начале алгоритма)\n", "a = 1 # первое число\n", "b = 1 # второе число\n", "for i in range(10):\n", " # тело цикла: выполнится 10 раз\n", " c = a + b\n", " a = b\n", " b = c\n", " print(i, c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Другой пример: допустим, мы бы хотели не выводить найденные числа Фибоначчи на экран, а записать их в некоторый список. Для этого немного модифицируем код выше: вместо команды `print()` нужно подставить команду, которая добавит найденное число в некоторый список. Однако, чтобы было, куда добавлять, этот список должен быть создан заранее. Изначально он может быть и пустым — такой список обозначается пустыми квадратными скобками." ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418]\n" ] } ], "source": [ "a = 1 # первое число\n", "b = 1 # второе число\n", "fib = []\n", "# создали пустой список fib\n", "\n", "for i in range(25):\n", " c = a + b\n", " a = b\n", " b = c\n", " fib.append(b) # записали элемент в конец списка fib\n", " \n", "print(fib)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Проверка условий\n", "В ходе выполнения программы иногда требуется в зависимости от каких-то условий выполнять тот или иной фрагмент кода. Например, если пользователь ввёл не те данные, которые от него просили (хотели положительное число, а получили отрицательное), то надо вывести ошибку и попросить ввести данные снова. Решение этой задачи разбивается на несколько шагов: сначала нужно проверить некоторое условие, а потом в зависимости от результата этой проверки выбрать, какой код выполнять. Давайте начнём с проверки условий." ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "6 < 8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Здесь мы спросили «Правда ли, что 6 меньше 8?». «Воистину так» — ответил Python на своём заморском языке. Слово `True`, которое он выдал — это не просто слово, означающее «истина», а специальное логическое значение. Его ещё называют «булевским» (по имени одного из основателей математической логики [Джоджа Буля](https://ru.wikipedia.org/wiki/%D0%91%D1%83%D0%BB%D1%8C,_%D0%94%D0%B6%D0%BE%D1%80%D0%B4%D0%B6)). Оно бывает всего двух видов: либо истина (`True`), либо ложь (`False`). Третьего не дано." ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 149, "metadata": {}, "output_type": "execute_result" } ], "source": [ "8 > 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Результат проверки можно записать в переменную." ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "collapsed": true }, "outputs": [], "source": [ "condition = 6 < 8" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "condition" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Говорят, что переменная `condition` теперь булевская (`bool`)." ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "bool" ] }, "execution_count": 153, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(condition)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно проверять равенство двух величин. Правда ли, что 7 равно 7?" ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 == 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Обратите внимание: здесь нужно написать символ равенства два раза, потому что один знак равно — это операция присвоения («присвоить то, что справа, тому, что слева»), а операция проверки равенства — это совсем другая штука. Например." ] }, { "cell_type": "code", "execution_count": 156, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Положили в `a` число `5`. Такая операция ничего не вернула." ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "collapsed": true }, "outputs": [], "source": [ "a = 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь положили в `a` число 7." ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a == 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь спросили, правда ли, что `a` равняется пяти. Получили `False`.\n", "\n", "Надо сказать, что сравнение работает достаточно разумным образом. Например, число `7` и число `7.0` — это, строго говоря, разные объекты (первое — это целое число, второе — число с плавающей запятой), но понятно, что как числа это один и тот же объект. Поэтому сравнение выдаст `True`." ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 160, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 == 7.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Оператор if" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Хорошо, мы научились проверять разнообразные условия. Теперь нужно менять поведение программы в зависимости от результатов такой проверки. Например, мы хотим ввести число с клавиатуры и в случае, если оно оказалось отрицательным, сообщить об ошибке. Для этого нужно использовать конструкцию `if`." ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите положительное число: -4\n", "Ошибка!\n", "Число не является положительным!\n", "Вы ввели -4\n" ] } ], "source": [ "a = int(input(\"Введите положительное число: \"))\n", "if a < 0:\n", " print(\"Ошибка!\")\n", " print(\"Число не является положительным!\")\n", "print(\"Вы ввели\", a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Нужно обратить внимание на несколько вещей: во-первых, после `if` указывается условие, а после условия обязательно ставится двоеточие (как и в циклах), дальше идёт блок команд, которые выполняются в том случае, если условие верно (то есть является `True`). Как и в циклах, этот блок команд должен быть выделен отступом. Команды, не входящие в блок (в данном случае это последняя строчка) выполняются в любом случае." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Допустим, мы хотим обработать отдельно обе ситуации: когда условие выполняется и когда оно не выполняется. Для этого нужно использовать ключевое слово `else`." ] }, { "cell_type": "code", "execution_count": 163, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите положительное число: 22\n", "Как хорошо!\n", "Вы ввели положительное число!\n", "Вы ввели 22\n" ] } ], "source": [ "a = int(input(\"Введите положительное число: \"))\n", "if a < 0:\n", " print(\"Ошибка!\")\n", " print(\"Число не является положительным!\")\n", "else:\n", " print(\"Как хорошо!\")\n", " print(\"Вы ввели положительное число!\")\n", "print(\"Вы ввели\", a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Конструкция `if-else` работает как альтернатива: выполняется либо один фрагмент кода (после `if` — если условие верно), либо другой (после `else` — если неверно). Иногда нужно проверить несколько условий подряд." ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите какое-нибудь число: 123\n", "Это очень большое число\n" ] } ], "source": [ "a = int(input(\"Введите какое-нибудь число: \"))\n", "if a > 100:\n", " print(\"Это очень большое число\")\n", "elif a > 10:\n", " print(\"Это больше число\")\n", "else:\n", " print(\"Это маленькое число\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Здесь используется ключевое слово `elif`, являющееся объединением слов `else` и `if`. Логика такая: сначала выполняется первое условие (`a > 100`), если оно верно, то выполняется код после `if`, если неверно, то проверяется следующее условие (`a > 10`), если оно верно, то выполняется код после `elif`, если неверно, то выполняется код после `else`. Команда `else`, если она есть, всегда должна идти в конце. Блоков `elif` может быть много. Условия проверяются по очереди, начиная от первого; как только какое-то из условий оказывается верным, выполняется соответствующий блок и проверка остальных условий не производится." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Сложные условия\n", "Допустим, нам нужно проверить выполнение нескольких условий. Скажем, мы хотим получить число от 0 до 100 — числа меньше 0 или больше 100 нас не устраивают. Это можно было бы сделать с помощью нескольких вложенных операторов `if` примерно так." ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Пожалуйста, введите число от 0 до 100: 101\n", "Вы ошиблись, это не число от 0 до 100\n" ] } ], "source": [ "a = int(input(\"Пожалуйста, введите число от 0 до 100: \"))\n", "if a <= 100:\n", " if a >= 0:\n", " print(\"Спасибо, мне нравится ваше число\")\n", " else:\n", " print(\"Вы ошиблись, это не число от 0 до 100\")\n", "else:\n", " print(\"Вы ошиблись, это не число от 0 до 100\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Этот код довольно громоздок, строчку с сообщением об ошибке пришлось скопировать дважды. Не очень хорошо. Оказывается, можно реализовать тот же функционал проще." ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Пожалуйста, введите число от 0 до 100: 22\n", "Спасибо, мне нравится ваше число\n" ] } ], "source": [ "a = int(input(\"Пожалуйста, введите число от 0 до 100: \"))\n", "if a <= 100 and a >= 0:\n", " print(\"Спасибо, мне нравится ваше число\")\n", "else:\n", " print(\"Вы ошиблись, это не число от 0 до 100\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Здесь используется ключевое слово `and`, обозначающее операцию *логического И*. Оно делает следующее: проверяет левое условие (в данном случае `a <= 100`), проверяет правое условие (`a >= 100`) и если оба этих условия выполняются (то есть имеют значение `True`), то и результат выполнения `and` оказывается `True`; если же хотя бы одно из них не выполняется (то есть имеет значение `False`), то и результат выполнения `and` является `False`. Таким образом мы можем проверить в точности интересующее нас условие.\n", "\n", "> Строго говоря, если левый аргумент `and` оказывается ложью, то правый даже не вычисляется: зачем тратить время, если уже понятно, что возвращать надо ложь?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно было бы переписать этот код другим способом, используя логическое ИЛИ (`or`):" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Пожалуйста, введите число от 0 до 100: 22\n", "Спасибо, мне нравится ваше число\n" ] } ], "source": [ "a = int(input(\"Пожалуйста, введите число от 0 до 100: \"))\n", "if a > 100 or a < 0:\n", " print(\"Вы ошиблись, это не число от 0 до 100\")\n", "else:\n", " print(\"Спасибо, мне нравится ваше число\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Результат выполнения `or` является истиной в том случае, если хотя бы один аргумент является истиной. Наконец, есть третий логический оператор — это отрицание (`not`). Он имеет всего один аргумент и возвращает истину, если этот аргумент является ложью, и наоборот." ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Пожалуйста, введите число от 0 до 100: 22\n", "Спасибо, мне нравится ваше число\n" ] } ], "source": [ "a = int(input(\"Пожалуйста, введите число от 0 до 100: \"))\n", "if not (a <= 100 and a >= 0):\n", " print(\"Вы ошиблись, это не число от 0 до 100\")\n", "else:\n", " print(\"Спасибо, мне нравится ваше число\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно проверить, как работают логические команды, просто подставляя в качестве аргументов `True` или `False`:" ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 169, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True or False" ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "False and True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно даже задать Python известный вопрос: быть или не быть?" ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 172, "metadata": {}, "output_type": "execute_result" } ], "source": [ "to_be = False\n", "to_be or not to_be" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Что будет, если `to_be` сделать равным `True`?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Цикл while" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "До сих пор получив неправильное число (не удовлетворяющее нашим условиям) мы могли лишь поругать пользователя и развести руками. На самом деле программы обычно ведут себя иначе: они заставляют пользователя ввести правильное число, требуя этого снова и снова, пока он не капитулирует.\n", "\n", "Чтобы добиться такого эффекта (а заодно довести пользователя до белого каления) нужно использовать цикл `while`. В прошлый раз мы обсуждали цикл `for`, используемый для того, чтобы перебрать элементы некоторого списка, или, в качестве частного случая — выполнить некоторую операцию какое-то конечное (заранее известное) число раз. Цикл `while` нужен, если мы хотим выполнять что-то до тех пор, пока выполняется некоторое условие. Например, пока рак на горе не свистнет. Это делается следующим образом." ] }, { "cell_type": "code", "execution_count": 174, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите число от 0 до 100: -2\n", "Неверно! Это не число от 0 до 100\n", "Введите число от 0 до 100: -5\n", "Неверно! Это не число от 0 до 100\n", "Введите число от 0 до 100: 101\n", "Неверно! Это не число от 0 до 100\n", "Введите число от 0 до 100: 88\n", "Ну хорошо\n" ] } ], "source": [ "a = int(input(\"Введите число от 0 до 100: \"))\n", "while a>100 or a<0:\n", " print(\"Неверно! Это не число от 0 до 100\")\n", " a = int(input(\"Введите число от 0 до 100: \"))\n", "print(\"Ну хорошо\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Другой пример: проверка пароля." ] }, { "cell_type": "code", "execution_count": 175, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Please, enter password: 12345\n", "Access denied\n", "Please, enter password: mypassword\n", "Access denied\n", "Please, enter password: ;ugliugliug\n", "Access granted\n" ] } ], "source": [ "correct_passwd = ';ugliugliug'\n", "passwd = input(\"Please, enter password: \")\n", "while passwd != correct_passwd:\n", " print(\"Access denied\")\n", " passwd = input(\"Please, enter password: \")\n", "print(\"Access granted\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Этот код не очень изящный, потому что нам приходится дважды писать строку с `input()`. Ситуация, при которой нам приходится копировать какие-то строчки кода, обычно означает, что допущена ошибка в проектировании. Можно сделать это более изящно с помощью команды `break` — она позволяет выйти из цикла. Следующий пример также демонстрирует бесконечный цикл: по идее `while True:` должен выполняться до тех пор, пока `True` это `True`, то есть вечно. Но мы выйдем раньше с помощью `break`." ] }, { "cell_type": "code", "execution_count": 176, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Please, enter password: ;ugliugliug\n", "Access granted\n" ] } ], "source": [ "correct_passwd = ';ugliugliug'\n", "while True:\n", " passwd = input(\"Please, enter password: \")\n", " if passwd == correct_passwd:\n", " print(\"Access granted\")\n", " break\n", " else:\n", " print(\"Access denied\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Команду `break` можно использовать для выхода из любого цикла. Вот пример для цикла `for`:" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7\n", "9\n", "10\n", "7\n", "Negative number detected!!!!111111odin\n" ] } ], "source": [ "numbers = [6, 8, 9, 6, -7, 9]\n", "for i in numbers:\n", " if i<0:\n", " print(\"Negative number detected!!!!111111odin\")\n", " break\n", " print(i+1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Угадай-ка!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Напишем простую игру: в ней компьютер будет загадывать число от 1 до 100, а пользователь должен его угадать с нескольких попыток. В ответ на каждую попытку компьютер сообщает, какое число больше: загаданное или предложенное пользователем.\n", "\n", "Для начала нам нужно подключить *генератор случайных чисел*, чтобы иметь возможность «загадать» число." ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from random import randrange" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `randrange()` возвращает случайные (строго говоря, *пседослучайные*) числа из заданного диапазона." ] }, { "cell_type": "code", "execution_count": 180, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 180, "metadata": {}, "output_type": "execute_result" } ], "source": [ "randrange(1,101)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь можно приступить к нашей программе." ] }, { "cell_type": "code", "execution_count": 181, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ваша попытка: 70\n", "Слишком маленькое число\n", "Ваша попытка: 80\n", "Слишком большое число\n", "Ваша попытка: 75\n", "Слишком маленькое число\n", "Ваша попытка: 78\n", "Слишком большое число\n", "Ваша попытка: 77\n", "Победа!\n" ] } ], "source": [ "a = randrange(1, 101)\n", "while True:\n", " guess = int(input(\"Ваша попытка: \"))\n", " if guess == a:\n", " print(\"Победа!\")\n", " break\n", " elif guess > a:\n", " print(\"Слишком большое число\")\n", " else:\n", " print(\"Слишком маленькое число\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Это, конечно, не Half Life, но тоже играть можно. (Кстати, какой оптимальный алгоритм игры?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Нумерация элементов списка" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В качестве небольшого отступления обсудим такую задачу: есть список, нужно вывести его элементы и их индексы. Эту задачу можно было бы решать так:" ] }, { "cell_type": "code", "execution_count": 182, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 7\n", "1 8\n", "2 9\n", "3 43\n" ] } ], "source": [ "numbers = [7, 8, 9, 43]\n", "i = 0 # здесь будет храниться индекс\n", "for n in numbers:\n", " print(i, n)\n", " i += 1 \n", " # эта строчка эквивалентна i = i + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Не самое изящное решение — приходится вводить какую-то переменную `i`, инициализировать её до входа в цикл и ещё не забывать прибавить к ней единицу внутри цикла.\n", "\n", "Другой вариант:" ] }, { "cell_type": "code", "execution_count": 183, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 7\n", "1 8\n", "2 9\n", "3 43\n" ] } ], "source": [ "numbers = [7, 8, 9, 43]\n", "for i in range(len(numbers)):\n", " print (i, numbers[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Тоже не является верхом изящества: здесь нам приходится каждый раз писать `numbers[i]` и вообще понятность страдает: глядя на цикл `for` неясно, что мы собираемся перебирать элементы списка `numbers`.\n", "\n", "Правильный Python'овский подход выглядит вот так:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 7\n", "3 8\n", "4 9\n", "5 43\n" ] } ], "source": [ "numbers = [7, 8, 9, 43]\n", "for i, n in enumerate(numbers, 2):\n", " print(i,n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Что здесь произошло. Главная магия кроется в команде `enumerate()`. Давайте посмотрим, как она работает:" ] }, { "cell_type": "code", "execution_count": 184, "metadata": { "collapsed": false }, "outputs": [], "source": [ "enum = list(enumerate(numbers))" ] }, { "cell_type": "code", "execution_count": 185, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[(0, 7), (1, 8), (2, 9), (3, 43)]" ] }, "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enum" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В результате выполнения `enumerate` возвращается штука, которая ведёт себя как список, элементами которого являются пары чисел (вообще-то это кортежи, `tuple`, то есть неизменяемые списки). В каждой паре первое число — это индекс, а второе — элемент исходного списка.\n", "\n", "Дальше, при выполнении цикла `for` используется механизм списочного присваивания. Работает он так." ] }, { "cell_type": "code", "execution_count": 186, "metadata": { "collapsed": true }, "outputs": [], "source": [ "a, b = (7, 9)" ] }, { "cell_type": "code", "execution_count": 187, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7\n", "9\n" ] } ], "source": [ "print(a)\n", "print(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если в левой части от знака равенства стоит несколько переменных, разделённых запятыми, а в правой — какой-нибудь объект, ведущий себя как список, причём число его элементов равно число переменных, то присваивание идёт поэлементно: первый элемент списка в первую переменную, второй во вторую и т.д." ] }, { "cell_type": "code", "execution_count": 191, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] } ], "source": [ "a, b, c = (1, 2, 3)\n", "print(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь обратите внимание, что в цикле `for` у нас в качестве переменной цикла указаны две переменные (через запятую). Что произойдёт, когда мы попадём в цикл в первый раз? `for` возьмёт первый элемент от `enumerate(numbers)`. Это пара чисел:" ] }, { "cell_type": "code", "execution_count": 188, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(0, 7)" ] }, "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enum[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь он приравняет эту пару чисел к паре переменных: `i` и `n`:" ] }, { "cell_type": "code", "execution_count": 189, "metadata": { "collapsed": true }, "outputs": [], "source": [ "i, n = enum[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь в `i` лежит `0` (индекс первого элемента `numbers`, а в `n` — сам первый элемент `numbers`. И так далее, на каждом шаге в `i` будет лежать соответствующий индекс, а в `n` соответствующее число.\n", "\n", "При этом результат получается гораздо более изящным, чем предыдущие: нет необходимости как-то явно описывать происходящее с `i`, и смысл происходящего понятен при взгляде на строчку с `for`.\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.5.0" } }, "nbformat": 4, "nbformat_minor": 0 }