Как учиться

Переменные и типы данных

На экране 15090, а ждали 240. Программа не упала, не написала ни одного сообщения об ошибке, отработала до конца и выдала число с самым уверенным видом. Код при этом три строки: спросить первую цену, спросить вторую, вывести сумму. Ученик вводил 150 и 90 — цены двух тетрадей в воображаемой кассе, — и сложение вместо суммы дало склейку. Это одна из первых настоящих ошибок в самом начале школьной информатики, и на ней хорошо видно, зачем вообще нужны типы данных. Пройдём её по порядку, а в конце дойдём до сдачи с покупки и до вопроса, почему деньги в программах лучше не хранить дробными числами.

Что лежит в переменной

Вот эти три строки, записанные подряд: a = input(), затем b = input(), затем print(a + b). Функция input() ждёт, пока пользователь введёт что-нибудь и нажмёт Enter, а результат кладёт в переменную. Переменная — это имя, за которым хранится значение: сначала записали, потом обратились по имени, и значение достали.

Прежде чем что-то чинить, спросим программу, что у неё лежит. Добавляем строку print(type(a)) и получаем сообщение о том, что это тип «str», то есть строка. Для программы «150» — не число сто пятьдесят, а три знака подряд: единица, пятёрка и ноль, как слово из трёх букв.

И тогда сложение перестаёт быть загадкой. Для строк плюс означает склеить: "150" + "90" даёт "15090", как «до» плюс «свидания» даёт «досвидания». Программа сложила именно то, что получила, и вопрос не к ней.

Функция input() всегда возвращает строку, даже если пользователь ввёл одни цифры: она не знает, чего вы ждёте, и не пытается угадать.

Раз уж речь о переменных, несколько слов об их именах. Пока программа в три строки, а и b сходят за имена. В программе на двадцать строк вы уже не вспомните, что хранилось в a, и ошибка переставленных переменных ловится только глазами. Поэтому price и paid лучше, чем a и b, а change лучше, чем x. Python допускает и русские имена, но в учебниках и в чужом коде принято писать латиницей, и привычку стоит заводить сразу.

Отдельная странность, на которой запинаются те, кто хорошо знает математику: x = x + 1 там было бы неверным уравнением, а в программе это команда «взять то, что лежит в x, прибавить единицу и положить обратно». Знак «равно» здесь читается как «присвоить». С этим читают справа налево: сначала вычисляется правая часть, потом результат записывается в левую.

Превратить текст в число

Чинится это в одном месте: сразу при чтении. Было a = input(), стало a = int(input()). Функция int() берёт текст из цифр и возвращает целое число, с которым уже можно считать. Те же две строки для b, и print(a + b) выдаёт 240.

Здесь есть ловушка, на которую попадают почти сразу. Если ученик введёт «150.5» или «3,5», программа упадёт с ошибкой ValueError: int() принимает только целое, записанное цифрами. У этой ошибки есть сообщение, и по нему видно место, а склейка выше сообщения не дала.

Заодно маленькая приятность: int() сам отбрасывает пробелы и перевод строки по краям, так что лишний пробел после «150» ему не мешает. Хуже, когда пробел оказывается в середине: «150 90», введённое одной строкой, вызовет ту же ошибку, потому что для int() это не одно число. Такой ввод в задачах встречается часто, и его разбирают отдельным приёмом: сначала режут строку на части по пробелам, потом превращают каждую часть в число. Запись этого приёма короткая, a, b = map(int, input().split()), и её полезно один раз выучить, не вдаваясь в устройство map: пока она нужна как заготовка, а смысл придёт вместе со статьёй про строки и списки.

Дробные числа и запятая

Цены бывают с копейками, поэтому следующий шаг — float(). Строка price = float(input()) принимает «99.90» и превращает в число 99.9. А «99,90», которое русскоязычный пользователь наберёт скорее всего, вызовет ту же ValueError: Python признаёт только точку.

Здесь две стратегии, и выбор между ними стоит обсудить. Можно научить программу заменять запятую на точку: text.replace(",", ".") перед преобразованием. Можно оставить как есть и написать в запросе «введите цену через точку». Для учебной программы подойдёт вторая, для настоящей нужна первая, и разница между ними хорошо показывает, чем отличается «работает у меня» от «работает у других». Как устроены такие операции над текстом, разобрано в статье про строки и работу с текстом.

Четыре типа на первое время

Типов в Python много, но в первые месяцы хватает четырёх: int — целые числа, float — дробные, str — строки, bool — «да» или «нет», то есть истина и ложь. Результат любого сравнения как раз булевый: 5 > 3 даёт True. Эти значения управляют условиями, и о них речь в статье про ветвления.

Типы не смешиваются молча. Попробуйте вывести подпись и сдачу одной операцией, print("Сдача: " + 360): программа упадёт с ошибкой TypeError, потому что строку и число складывать нельзя. Лечится двумя способами. Либо превратить число в текст, "Сдача: " + str(360), либо дать print два аргумента через запятую, print("Сдача:", 360), и тогда он сам поставит между ними пробел и ничего не склеит. Второй способ короче, и им пользуются чаще.

Полезная привычка на первые недели: после каждого преобразования печатать и значение, и тип, и убирать эту строку только когда в программе не осталось сомнений. Выглядит как лишняя работа, пока не потратишь вечер на поиск ошибки, которую выдала бы одна строка с type().

Деньги: целые или дробные

Хранить деньги в дробных числах не надо, даже если на двух примерах всё сходится. Проверьте сами: print(0.1 + 0.2) выдаёт 0.30000000000000004. Компьютер хранит дробные числа в двоичной записи, и многие привычные десятичные дроби в ней не представимы точно, как одна треть не записывается конечной десятичной дробью. Для расчёта площади комнаты это не страшно. Для сравнения «хватает ли денег» страшно: сумма, которая на бумаге равна ровно цене, в программе может оказаться на волосок больше или меньше.

Рабочий выход для школьных программ простой: держать деньги целым числом копеек. Цена 99,90 — это 9990 копеек. Сложение, вычитание и сравнение целых чисел точны, и на экран рубли с копейками выводят только в самом конце. Перевести введённую цену в копейки можно так: умножить её на 100 и округлить результат функцией round(), которая убирает двоичные хвосты.

У Python есть и специальный тип для точных десятичных вычислений, он называется Decimal. В школьном курсе он не нужен, и ученик, который с ним возится в самом начале, чаще всего прячется от настоящей темы. Копеек целыми хватает надолго.

Сдача: целое деление и остаток

Теперь решим задачу, ради которой всё затевалось. Покупатель платит 1000 рублей, чек на 640. Сдача — 360, и кассиру нужно знать, сколько сотенных и сколько остаётся мелочью. Для этого в Python две операции, которых нет в обычной арифметике, а в школьной они называются иначе: целая часть от деления и остаток.

Запись 360 // 100 даёт 3 — столько раз сотня помещается в сдачу. Запись 360 % 100 даёт 60 — столько остаётся. Обычное деление 360 / 100 даёт 3.6: слеш всегда возвращает дробное число, даже если делится без остатка, и 10 / 2 выдаёт 5.0 с точкой. На это спотыкаются в заданиях, где ответ нужен целым.

Если кассир выдаёт сдачу купюрами и монетами по 500, 100, 50 и 10 рублей, то для каждого номинала повторяется одна и та же пара действий: целая часть показывает, сколько банкнот, остаток идёт дальше. Для работы с несколькими номиналами понадобится цикл, и это естественный переход к статье про циклы; проверка «хватило ли денег» требует условия, о нём в статье про условия и ветвления. Там же разобрана родственная ловушка: сравнение введённых чисел без int(), при котором "10" < "9" оказывается верным.

Проверить себя можно такой задачей: программа спрашивает цену тетради в рублях (с копейками, через точку) и количество тетрадей, а выводит стоимость покупки в копейках, целым числом. Умножение в Python записывается звёздочкой. Если для цены 99.90 и пяти штук вышло 49950, всё сделано верно; если 49950.0, то пропущено округление до целого; а если программа упала, по тексту ошибки обычно видно, какой шаг из этой статьи потерян. Если не выходит, репетитору в чате пригодится строка вида «ввёл 150 и 90, получил 15090, ждал 240» вместе с файлом программы.

Когда это встретится на экзамене

Типы данных — не школьная формальность. Ответы на экзамене вводятся в специальную программу, формат ответа (число, слово) задан в условии, и ошибка в виде лишнего пробела или точки вместо запятой стоит балла. Работа с типами пригодится и в первой части: например, int("101", 2) переводит двоичную запись в обычное число 5, а задачи на системы счисления там встречаются (какие именно задания, смотрите в карте первой части и в демоверсии своего года). Общее устройство экзамена описано в статье о структуре ЕГЭ по информатике.

Рядом по теме стоит статья про алгоритмы и блок-схемы: там порядок действий записывается до того, как он стал кодом, и к переменным удобно возвращаться оттуда.

Начать поиск:

Вопросы и ответы

Почему input() не может сразу вернуть число? Потому что пользователь может ввести что угодно: слово, пустую строку, число с запятой. Программа не знает заранее, что вы ждёте, и отдаёт всё как текст, а превращать его в число или нет, решает автор. Это неудобно на первой неделе и спасает потом.

Что будет, если вместо числа ввести слово? Программа остановится с ошибкой ValueError на строке с int() или float(). Для школьных задач вход считается таким, как сказано в условии, и защита от неверного ввода откладывается: она требует конструкции try и except, которая идёт заметно позже.

Нужно ли учить Decimal? Пока нет. Копейки целыми числами закрывают всё, что встретится в школьных задачах.

Оставить заявку

Ещё в рубрике «Как учиться»