#!/usr/bin/env python # coding: utf-8 # # Основы программирования в Python # # *Алла Тамбовцева, НИУ ВШЭ* # # *Данный ноутбук основан на [лекции](http://nbviewer.math-hse.info/github/ischurov/pythonhse/blob/master/Lecture%203.ipynb#%D0%9F%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0-%D1%83%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D0%B9) Щурова И.В., курс «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ).* # ## Условные конструкции, множества, цикл `while` # ### Проверка условий # Начнем с известных всем операторов. Проверим, # # * правда ли, что 8 меньше 9; # * правда ли, что 9 больше 10. # In[1]: 8 < 9 # правда # In[2]: 9 > 10 # неправда # Результат такой проверки имеет логический тип (*boolean*). # In[3]: res = 8 < 9 res # Как мы уже обсуждали, переменные такого типа могут принимать два значения True или False. Обратите внимание, что True и False не заключены в кавычки ‒ добавив кавычки, мы получим строки "True" и "False". # In[4]: "True" == True # При проверке равенства двух частей (переменных, списков и так далее) используется двойной знак "равно". # In[5]: 6 == 6 # Одинарный знак "равно" используется для присваивания значений. Так ничего не сравним, но сохраним в переменную `a` число 6: # In[6]: a = 6 a # А так уже проверим условия: # In[7]: print(a == 6) print(a == 9) # Неравенство, то есть отрицание равенства, в Python обозначается с помощью оператора `!=` (вообще `!` в программировании используется для отрицания). # In[8]: 6 != 7 # Стоит отметить, что Python достаточно лояльно относится к разделению между типам данных. Например, если мы сравним целое число и то же число, но с плавающей точкой (с дробной частью равной 0), Python сообщит, что эти числа совпадают. # In[9]: 6 == 6.0 # верно # ### Условные конструкции # Условные конструкции ‒ конструкции с операторами условия. Условная конструкция обычно предполагает "развилку": если условие выполняется, то должен выполняться один набор действий, если нет ‒ другой набор действий. Давайте напишем программу, которая будет просить пользователя ввести целое число, и если это число менее 10, на экран будет выводиться сообщение "Мало", иначе ‒ "Много". И заодно познакомимся с конструкцией *if-else*. # In[8]: x = int(input("Введите число: ")) # In[9]: if x < 10: print("Мало") else: print("Много") # В части с `if` мы прописываем условие, в зависимости от которого Python будет делать выбор, что выводить на экран, а после двоеточия перечисляем действия, которые будут выполняться в случае, если `x` удовлетворяет условию. В части с `else` мы уже не пишем никакого условия ‒ оператор `else` сам по себе означает "в случае, если условие в выражении с `if` не выполнено". # # Часть с `else` является необязательной: программа может существовать только с условием `if`. Тогда в случае невыполнения условия ничего происходить не будет, Python просто перейдет к следующим строкам кода. # Как быть, если условий несколько? Например, мы просим пользователя ввести оценку, и если оценка больше 10, на экране должно быть сообщение "Много", если ровно 10 ‒ "В самый раз", если меньше ‒ "Мало". Можно воспользоваться оператором `elif`, который по смыслу является сочетанием `else + if`: если предыдущее условие невыполнено, то, нужно проверить следующее условие, и если оно тоже не выполнено, то уже перейти к ветке с `else`. # In[10]: mark = int(input("Введите оценку: ")) # In[11]: if mark > 10: print("Много") elif mark == 10: print("В самый раз") else: print("Мало") # Ответвлений с `elif` может быть несколько: сколько условий, столько и выражений с `elif`. Добавим еще одно условие: # In[12]: if mark > 10: print("Много") elif mark > 6: print("Хорошо") elif mark > 4: print("Неплохо") else: print("Плохо") # Законный вопрос: а можно ли обойтись совсем без `elif`, просто записав несколько выражений с `if`? Тут все зависит от ситуации. Иногда решения использовать `elif` и `if` будут равнозначными. Если мы перепишем код в примере выше, заменив `elif` на `if`, ничего не изменится, так как условия будут проверяться последовательно в любом случае: если оценка больше 10, будет выведено слово "Много", если нет ‒ программа перейдет к следующему условию, и так далее. # In[13]: if mark > 10: print("Много") if mark > 6: print("Хорошо") if mark > 4: print("Неплохо") else: print("Плохо") # В случае, когда условия как-то связаны между собой, нужно быть более внимательными. Рассмотрим такой пример. # # **Случай 1.** # In[26]: if mark < 10: print("Это нормально") elif mark == 10: print("Отлично") if mark < 6: print("Плохо") # Если оценка меньше 10, мы выводим на экран сообщение "Это нормально", если нет, то проверяем, равна ли она 10: если да, то выводим "Отлично", если нет ‒ ничего не делаем. При этом, *после* всех этих действий делаем дополнительную проверку: если оценка меньше 6, выводим "Плохо". # **Случай 2.** # In[20]: if mark < 10: print("Это нормально") elif mark == 10: print("Отлично") elif mark < 6: print("Плохо") # Если оценка меньше 10, мы выводим на экран сообщение "Это нормально", если нет, то проверяем, равна ли она 10: если да, то выводим "Отлично", если нет ‒ сравниваем ее с 6. Если оценка меньше 6, выводим "Плохо". # Почему во втором случае мы не увидели сообщение "Плохо"? Потому что из-за второго `elif` мы попросту до него не дошли! На ветку со вторым `elif` мы попадаем в случае, если предыдущее условие не выполняется, то есть если оценка не равна 10. А на ветку с первым `elif` мы попадем, в случае, если оценка не менее 10. Получается, что мы должны выводить слово "Плохо" в случае, когда оценка более 10 и при этом менее 6, чего в природе не бывает. Использовав `elif` необдуманно, мы добавили лишнее условие, которое никогда не будет выполняться! Тут будет полезно вспомнить схемы, которые многие, наверное, видели на уроках информатики в школе. Запоминать их необязательно, просто они хорошо иллюстрируют различия между двумя случаями. # **Случай 1** # # ![title](1.png) # # **Случай 2** # # ![title](2.png) # Возможно, предыдущее обсуждение `if` и `elif` могло вас чуть-чуть запутать, но это не повод расстраиваться. Важно просто помнить, что разница между этими операторами есть. Остальное можно проверить экспериментально на конкретном примере :) # ### Сложные условия # Пусть у нас есть три целочисленные переменные `a`, `b` и `c`, и мы планируем составлять сложные, составные уcловия, касающиеся этих переменных. # In[13]: a = 3 b = 7 c = 1 # Помогут операторы `and` и `or`. Оператор `and` соответствует одновременному выполнению условий, оператор `or` соответствует ситуации, когда хотя бы одно из условий выполняется. Оператор `or` в Python ‒ обычное "или", не исключающее: либо верно первое условие, либо второе, либо оба. # In[14]: (a < b) and (b > c) # оба верны # In[15]: (a < b) and (c > b) # второе неверно -> все неверно # In[16]: (a < b) or (a > c) # первое верное -> хотя бы одно верно # In[17]: (a < b) or (c > b) # первое верное -> хотя бы одно верно # Можем работать с элементами списков: # In[18]: l1 = [1, 3, 6, 8] l2 = [0, 9, 6, 8] # In[19]: l1[0] > l2[0] # 1 больше 0 # In[20]: (l1[0] > l2[0]) and (l1[2] == l2[2]) # оба верны # In[22]: (l1[0] > l2[0]) or (l1[2] == l2[2]) # оба верны # Давайте пройдемся по парам элементов в списках `l1` и `l2`, и если значения элементов, которые стоят на одном и том же месте, просто в разных списках, совпадают, мы будем выводить сообщение "It's true! They are equal!", а если нет ‒ сообщение "It's false! They are not equal!". # # Сначала посмотрим на длину списков: # In[23]: print(len(l1)) print(len(l2)) # Списки одинаковой длины, это хорошо! Напишем цикл. # In[24]: for i in range(0, len(l1)): if l1[i] == l2[i]: print("It's true! They are equal!") else: print("It's false! They are not equal!") # А теперь предлагаю вам такую задачу. Есть список оценок `marks`, и для каждой оценки нужно вывести комментарий (Отлично, Хорошо, Удовлетворительно, Плохо) с новой строки. # In[26]: marks = [2, 7, 8, 10, 5, 8, 1, 6] # **Решение:** # In[27]: for mark in marks: if mark >= 8: print("Отлично!") elif (mark >= 6) and (mark < 8): print("Хорошо!") elif (mark >= 4) and (mark < 6): print("Удовлетворительно!") else: print("Плохо!") # Можно написать аналогичный код, но оценку теперь будет вводить пользователь с клавиатуры. # In[28]: mark = int(input("Введите оценку: ")) if mark >= 8: print("Отлично!") elif (mark >= 6) and (mark < 8): print("Хорошо!") elif (mark >= 4) and (mark < 6): print("Удовлетворительно!") else: print("Плохо!") # *Небольшое лирическое отступление, не связанное с условиями.* В Python есть функция `eval()`, которая позволяет исполнять код, который записан в виде строки. Например, мы сохранили в текстовый файл какие-то списки. Открываем файл, и Python считывает наши списки как обычные строки: # In[29]: s = "[2,5,6,8]" # In[30]: s # Если мы применим к строке `s` функцию `eval()`, мы получим список. # In[31]: L = eval(s) L # Работает это не только для списков: # In[32]: eval("a == 8") # Внимание: использование функции `eval()` считается небезопасным в том смысле, что она исполняет любой код, заключенный в строке, не проверяя при этом, является ли код хорошим, правильным. Более корректно было бы использовать функцию `literal_eval()`, которую вы можете встретить в автоматических тестах в домашних заданиях. Эту функцию нужно импортировать из модуля`ast`: # In[33]: from ast import literal_eval # In[34]: literal_eval("[1,2,3]") # Кроме `and` и `or` в Python есть еще полезные операторы: оператор принадлежности `in` и оператор отрицания `not`. # Пусть у нас есть списки отличных, хороших, удовлетворительных и плохих оценок. # In[35]: excel = [8, 9, 10] good = [6, 7] sat = [4, 5] bad = [1, 2, 3] # Проверим, лежит ли оценка 8 в плохих: # In[36]: 8 in bad # Применим отрицание: # In[37]: 8 not in bad # верно! # ### Множества # Иногда возникает необходимость проверять разные отношения между двумя наборами элементов. Например, проверить, есть ли у двух списков общие элементы. Для этого можно воспользоваться уже известным оператором `in`: # In[38]: L1 = [1, 3, 4] L2 = [1, 4, 5, 6] # In[42]: for i in L2: if i in L1: print(i) # Но иногда удобно превратить списки в множества (*sets*) и выполнять операции, которые определены для множеств в математике: пересечение, объединение, разность и так далее. # # Важно иметь в виду, что в множествах (как в программировании, так и в математике) не может быть повторяющихся элементов. Поэтому, если в списке есть повторяющиеся значения, и они важны, превращать спискок в множество не стоит, так как можно потерять элементы. # In[43]: numbers = [1, 1, 0, 2, 4, 3, 3] # 7 элементов # In[44]: set(numbers) # в множестве осталось 5 элементов + отсортированы по возрастанию # Множества из строк тоже существуют (вообще их можно создавать из любых объектов): # In[45]: words = ["one", "one", "two", "three"] # пока список # In[46]: set(words) # множество # А теперь превратим два числовых списка в множества A и B и попробуем произвести некоторые операции. # In[47]: A = set([0, 2, 4, 7]) B = set([0, 1, 4, 5]) # Пересечение множеств A и B ‒ общие элементы этих двух множеств: # In[48]: B.intersection(A) # метод intersection # Объединение множеств A и B ‒ все элементы множества A и множества B (но, конечно, без повторений): # In[66]: A.union(B) # метод union # Разность множеств B и A ‒ все элементы множества B, которых нет в A. # In[49]: B.difference(A) # метод difference # *Обратите внимание:* результат, полученный после использования методов для множеств, тоже является множеством ‒ элементы перечислены в фигурных скобках. Но при желании результат можно превратить в список: # In[68]: list(A.difference(B)) # Множество в Python, наверное, не такой распространенный объект как список, кортеж или словарь (о них поговорим позже), но знать о них полезно, потому что в некоторых задачах использовать методы для множеств гораздо эффективнее, чем писать какие-то циклы с разными условиями. # ### Цикл `while` # С циклом `for` мы уже знакомы. Сейчас мы познакомимся с циклом `while`, логика которого отличается от `for`. Конструкции с циклом `while` устроены следующим образом: действия, которые указаны в теле цикла, должны выполняться до тех пор, пока верно условие, прописанное после `while` (отсюда и название). Если в цикле `for` мы указывали некоторый промежуток, по которому в ходе цикла мы будем "пробегаться", то в случае с циклом `while` мы просто фиксируем стартовую точку, а конечную точку никак не указываем: программа сама остановится, когда условие в цикле перестанет выполняться. # In[50]: nums = [1, 0, 9, 10, -1, 8] # Давайте, используя цикл `while`, будем выводить на экран элементы списка `nums` до тех пор, пока не столкнемся с отрицательным значением. # In[51]: i = 0 # начинаем с индекса i=0 while nums[i] >= 0: # пока элемент nums[i] >= 0 print(nums[i]) # выводим элемент на экран i = i + 1 # переходим к следующему элементу # На значении 10 мы остановились: за ним идет значение -1, для которого условие `nums[i] > = 0` не выполняется. # # Давайте теперь попробуем переписать код так, чтобы он работал точно так же, но только чтобы в нем использовался цикл `for`, а не `while`. Вообще почти любой код с `while` можно переписать через `for`, и иногда это полезно: код с циклом `while` обычно более медленный, плюс, склонен к зацикливанию. # In[72]: for n in nums: if n >= 0: print(n) else: break # выходим из цикла # В коде выше мы использовали оператор `break`, который позволяет выйти из цикла, то есть закончить исполнение строк кода в теле цикла и перейти к коду дальше. # # А теперь напишем маленькую игру-угадайку. Программа будет загадывать целое число от 1 до 100, а пользователь его угадывать. Как программа будет загадывать число? Выбирать случайным образом из интервала [1, 100] (на самом деле псевдослучайным образом, так как абсолютной случайности не получится, генерирование чисел происходит по фиксированным алгоритмам). # In[53]: from random import randrange # импортируем модуль для функии randrange # In[54]: n = randrange(1, 101) # n и есть загаданное число # Осталось написать цикл. До тех пор, пока пользователь не угадает число, программа не будет останавливаться, но зато она будет давать подсказки: если введенное пользователем число больше загаданного, то будет выводиться сообщение "Вы ввели слишком большое число.", если меньше ‒ "Вы ввели слишком маленькое число." # In[55]: while True: guess = int(input("Ваша попытка:")) if guess == n: print("Вы выиграли!") break elif guess > n: print("Вы ввели слишком большое число.") else: print("Вы ввели слишком маленькое число.") # В коде выше в `while` мы не написали никакого условия явно, вместо этого мы написали `while True`. Это выражение означает "до тех пор, пока мы не вышли из цикла". В нашем случае это равносильно "до тех пор, пока не столкнулись с `break`, пока наш ответ не совпал с загаданным числом.