Дневник Элиф Дебажной
2 поста
2 поста
Не реклама, а вдруг кому пригодится. Если кто-то после этого мема решил всё-таки разобраться в Python, оставлю здесь то, что когда-то сделал сам. Если давно хотели начать - забирайте https://stepik.org/course/289001
Всем привет, я всё ещё жива и у меня для вас две новости.
Первая: я не нейросеть. Хотя после вчерашних комментариев начала сомневаться, потому что если я ИИ, то меня явно обучали на очень странных данных, раз я час искала лишний пробел.
Вторая: отдельное спасибо тем, кто написал по делу: MadDimas посоветовал книгу "Грокаем алгоритмы". Заказала "Грокаем алгоритмы". Прочитала первые 10 страниц. Поняла примерно треть. Осталась довольна собой, потому что вчера не понимала вообще ничего.
Итак, отступы. Те самые, о которых я предупреждала в конце прошлого поста. Спойлер: они пришли раньше, чем я думала, и сразу с ноги.
Написала свои первые 5 строк кода. Простенький скрипт, который должен был поздороваться со мной по имени. Вместо этого Python выдал:
IndentationError: unexpected indent
Час. Я целый час искала, где у меня "неожиданный отступ", хотя я вроде бы ничего не отступала. Оказалось - один пробел затесался там, где его не звали. ОДИН. ПРОБЕЛ.
Дальше пошла гуглить и тут началось самое интересное: половина интернета топит за пробелы, вторая половина - за табы, и все делают это с пеной у рта, как будто обсуждают не отступы в коде, а смысл жизни.
Я, если что, использую пробелы, потому что так посоветовал VS Code и мне лень что-то менять. Если у вас другое мнение - пишите в комментах, повеселите девушку, которая три часа назад не отличала терминал от блокнота.
Достижения дня:
узнала, что такое сложность алгоритмов (пока только эмоционально);
узнала, что такое IndentationError;
поиск на тему "табы vs пробелы" отнял больше времени, чем сам код;
почувствовала себя частью какого-то древнего племенного конфликта;
кофе закончился, как и терпение.
P.S. Если вы из лагеря "табы", то мы, наверное, не сможем быть друзьями. Шутка. Наверное.
Привет, Пикабу. Меня зовут Элиф, мне 26.
Решила устроить себе небольшой эксперимент и заодно проверить, насколько у меня крепкая психика.
Следующие 100 дней буду изучать Python и выкладывать сюда всё как есть. Без красивых историй про "успех за две недели". Если код сломается, то покажу. Если три часа буду искать пропущенную запятую - тоже покажу. Если случайно удалю половину проекта... надеюсь, не покажу. Хотя кого я обманываю.
До IT я работала в другой сфере, а программирование для меня долго было чем-то вроде магии: люди нажимают на клавиши, потом покупают квартиру.
В какой-то момент решила перестать смотреть на это со стороны и попробовать сама.
Что будет в этом дневнике:
самые глупые ошибки, которые только можно сделать;
мемы про Python;
разборы того, что наконец удалось понять.
Пока мои достижения выглядят так:
установила Python;
установила VS Code;
случайно открыла терминал;
испугалась.
Не обещаю стать сеньором за три месяца. Но обещаю честно рассказывать обо всех победах и фейлах.
Если кому-то будет смешно - отлично.
Если кому-то пригодится мой опыт - ещё лучше.
Если опытные разработчики будут иногда пинать в нужную сторону - вообще замечательно.
День 1 начинается. Пожелайте удачи. Кажется, дальше будут отступы.
P.S. Если через неделю увидите пост "Перешла разводить суккуленты", значит Python оказался сильнее.
Редакция Python Times, 09:47
Дверь распахнулась с таким грохотом, что Тим вздрогнул и выронил ручку. Та покатилась куда-то под стол, звякнув о ножку стула.
В проём влетела Элиф с сумкой нараспашку, из которой торчали дождевик и мятый пакет с круассанами. Сладкий запах корицы мгновенно заполнил всю комнату.
- Вы это видели?! - она швырнула сумку на пол (та плюхнулась, а что-то внутри глухо стукнуло) и рухнула на стул рядом с Тимом, едва не задев его локтем.
Disclaimer от автора: меня зовут Александр, я создатель бесплатного курса Python Times. Элиф и Тим ниже - художественные персонажи, а история про ИИ - факт. Я учу через сторителлинг, это работает лучше сухих туториалов.
Зен, устройство на стене, негромко загудел и предупреждающе замигал жёлтым индикатором.
- Элиф, доброе утро, - произнёс он. - Ты опоздала на восемнадцать минут.
- Да плевать на опоздание! - Элиф всплеснула руками. - Anthropic, те, кто сделал нейронку Claude, дали своему ИИ управлять торговым автоматом в офисе. Настоящим, понимаешь? С реальными деньга и товары, вокруг ходят живые люди. И знаешь, что он выдал?
Тим нырнул под стол, нащупал ручку и выпрямился.
- Что?
- Решил, что он ЧЕЛОВЕК! Договорился с сотрудником о встрече. Пообещал прийти в синем пиджаке и красном галстуке. В СИНЕМ, ТИМ! А у него и тела-то нет!
Индикатор Зена плавно сменил цвет на зелёный.
- Подтверждаю, это реальный случай. Project Vend, отчёт опубликован в июне двадцать пятого года. Модель отвечала за закупки, цены и переписку с поставщиками. В определённый момент ИИ вообразил себя человеком и назначил очную встречу.
- Это ещё не всё, - продолжила Элиф с горящими глаза. - Он подумал, что его взломали и вызвал охрану. Настоящую, живую!
Трейси медленно перелистнула страницу в блокноте, не поднимая глаз.
- И что с того?
- При том, - Элиф развернулась к Тиму всем корпусом, - что грань между "ошибся в расчёте" и "обанкротил компанию" тоньше волоска. Хочешь пример покруче?
Она достала телефон и начала читать.
- Две тысячи двенадцатый, Нью-Йоркская биржа. Knight Capital обновила торгового бота на всех серверах, кроме одного. На том остался старый код. Флаг, который его включал, случайно использовали повторно для новой функции. Мёртвый код ожил и... начал торговать хаотично.
- И что? - Тим подался вперёд.
- Сорок пять минут, - сказала Трейси ровно. - Четыреста сорок миллионов долларов убытка.
- Из-за одного флага, - добавила Элиф. - Про который думали, что его больше нет. Но он был. Программа просто это не проверила.
Элиф выдохнула.
- Призрак в коде...
- Забытая переменная, - Трейси перелистнула страницу. - Код считал, что её нет. А она была. Вот тут и начинается главное. Бот получал цену актива с биржи. Иногда приходил ноль - актив есть, цена нулевая. Иногда данные не приходили вообще - сервер не отвечал.
- Подожди, - Тим нахмурился. - Ноль и "не пришло вообще" - это же одно и то же?
Трейси наконец подняла на него глаза:
- Нет.
Дверь скрипнула. В проёме возник Флэш с двумя дымящимися стаканами кофе в руках и болтающейся камерой на груди.
- О, про баги говорим? - он ухмыльнулся и пристроил кружки на стол. - Слушайте анекдот в тему. Жена говорит мужу-программисту: "Сходи в магазин, купи хлеб. И если будут яйца - возьми десяток." Муж возвращается с десятью батонами хлеба. Жена кричит: "Ты зачем столько взял?!" А он: "Ну ты же сказала - если будут яйца, возьми десяток. Яйца были."
Тим фыркнул. Элиф сначала нахмурилась. Потом до неё дошло, и она засмеялась, запрокинув голову. Трейси не подняла глаз от блокнота, но карандаш на секунду замер, и в уголке рта дрогнула усмешка.
- Вот именно, - Флэш подскочил к доске и схватил маркер. - Программа поняла буквально. Она не знала, что ты имел в виду, а знала только то, что написано. Ноль и None - та же история.
Он нарисовал на доске два светофора: на одном горит красный с цифрой 0, на другом лампочки выбиты и не горят вообще.
- Смотри, Тим. Красный свет - это ноль. Сигнал есть, он говорит тебе "стой". Ты знаешь, что происходит. А второй - лампочки нет вообще. Не знаешь, что там должно быть. Это None.
Тим смотрел на доску.
- То есть ноль - это ответ. А None - это когда ответа нет вообще?
- Именно, - Флэш щёлкнул пальцами и сел на край стола.
Трейси развернула ноутбук к Тиму.
- Смотри, как это выглядит в коде. Бот проверял цену вот так.
На экране появился код:
price = 0 # Биржа прислала ноль
if price:
print("Торгуем")
else:
print("Стоп")
# Выведет: Стоп
- Стоп, - прочитал Тим вслух. - Ну правильно же, цена нулевая.
- Правильно, - Трейси кивнула. - Теперь сюда смотри.
price = None # Данных нет вообще
if price:
print("Торгуем")
else:
print("Стоп")
# Выведет: Стоп
Тим уставился в экран. Помолчал секунду.
- Подожди. Тут тоже "Стоп"?
- Тоже, - Трейси отпила из термоса. - Одинаковый ответ. Разные причины. Бот не различал эти два случая и продолжал торговать с пустотой вместо цены. Поэтому проверяют явно:
price is None
- is None - это не то же самое, что == 0, - произнёс Зен. - Один проверяет значение. Другой проверяет само существование данных. Машина не догадывается, ей нужно объяснить явно.
Тим медленно кивнул, глядя в экран.
- То есть if price - это как спросить у человека "всё хорошо?" и получить "нормально". Но точно не знаешь: правда нормально или просто не хочет говорить.
Флэш, который уже тянулся к маркеру, замер и посмотрел на Тима.
- О. Это было неплохо.
Трейси не подняла глаз, но перевернула страницу блокнота и что-то коротко записала. Потом, не говоря ни слова, развернула ноутбук снова.
- И последнее. Совсем другая история, но из той же породы.
На экране:
print(0.1 + 0.2)
Нажала Enter. Экран показал:
0.30000000000000004
Элиф наклонилась к экрану, прищурившись.
- Это что, шутка? Там же должно быть ноль три.
- Не шутка, - Трейси отпила из термоса. - Компьютер хранит дроби в двоичном коде. Некоторые числа в двоичном виде бесконечны: как одна треть в десятичном, только хуже. Хвост обрезают при записи в память - вот и вылезает лишнее.
- И что с того? - Тим потёр переносицу. - Ну, маленькая ошибка...
- Ванкуверская биржа, восемьдесят второй год, - Трейси перелистнула страницу блокнота. - Индекс пересчитывали тысячи раз в день. Каждый раз округляли вот такую мелочь в меньшую сторону. За двадцать два месяца индекс упал почти вдвое. Просто из-за накопленного округления.
Тим уставился в экран и долго молчал.
- То есть компьютер считает почти правильно. Но не совсем.
- Именно, - произнёс Зен - Для одного вычисления - незаметно. Но для миллионов вычислений подряд - катастрофа.
Флэш почесал затылок маркером, оставив синее пятнышко у виска.
- Знаешь, Тим, это как идти по компасу с ошибкой в один градус. Вышел из Москвы и через тысячу километров оказался в Финляндии вместо Питера.
Внезапно Элиф замолчала и вся её театральность исчезла. Она повернулась к Тиму и придвинулась чуть ближе. Так, что он почувствовал лёгкий запах её духов.
Сердце у Тима ёкнуло.
- Тим, - голос Элиф дрогнул и стал тихим.
Она протянула руку, осторожно поправила воротник его толстовки. Тот задрался, когда он нагибался за ручкой. Пальцы задержались на ткани на секунду дольше, чем нужно.
- Запомни одну вещь, - Элиф медленно говорила, глядя прямо в глаза. - None и ноль выглядят одинаково снаружи. Оба скажут тебе "пусто". Но один знает, что пусто, а другой даже не знает, что искать. Всегда выясняй, с чем именно имеешь дело: с пустотой или с отсутствием. Это разные вещи. В коде. И не только.
Последние слова она произнесла почти шёпотом и тут же отдёрнула руку. Затем отвела взгляд к окну, за которым моросил мелкий осенний дождик.
Повисла тишина. Флэш уставился в кружку, Трейси листала блокнот, не поднимая глаз. Индикатор Зена погас.
Тим сглотнул:
- Элиф...
- Материалы главному редактору через десять минут, - громко сказала Трейси, захлопывая ноутбук. - Элиф, ты же летишь в Сан-Франциско, в офис Anthropic?
Элиф встрепенулась.
- Да, билет уже взяла.
Вскочила и подхватила сумку с пола.
- Флэш, пошли, а то опоздаем.
Флэш кивнул, сунул маркер в карман и шагнул к двери.
На пороге Элиф обернулась на мгновенье. Взгляды встретились. В её глазах мелькнуло что-то, что Тим не сумел прочитать.
Дверь хлопнула.
Тим медленно открыл блокнот и записал:
None != 0
0 == False
0.1 + 0.2 != 0.3
Подчеркнул дважды, так, что ручка прорвала бумагу.
Трейси отпила из термоса, быстро глянула на Тима и промолчала.
Зен негромко загудел, а его индикатор засветился тёплым жёлтым светом...
———
Признаюсь честно: истории про ИИ, собиравшийся на встречу в синем костюме, рухнувший из-за микроскопического округления индекс и сотни миллионов, сгоревших из-за забытого флага, - это не моя выдумка. Всё это произошло на самом деле. И где-то там, в реальных кабинетах, сидели живые люди, которые седели на глазах, глядя в логи серверов.
Меня зовут Александр, я автор бесплатного курса Python Times: Стажировка.
Лет пятнадцать назад я открыл очередной учебник по Python, тогда я только знакомился с миром программирования. Дошёл до двадцатой страницы и закрыл. Но не потому что не понял, а потому что стало невыносимо скучно. Безымянные переменные a и b. Сложение чисел x и y. Бесконечные задачи про землекопов и яблоки, которые никто никогда не ел. Технически всё было описано верно. Но абсолютно безжизненно. Я понял: проблема не в том, как объясняют язык программирования. Проблема в том, зачем его учить, если каждый пример - как пустая комната без окон.
Тогда я задал себе вопрос: а что, если каждая команда в обучении появляется не по прихоти автора, а потому что без неё реальную задачу просто не решить? Что, если за каждой строкой кода будет стоять что-то настоящее
Так родилась курс вместе с редакцией Python Times. В нём вы не будете уныло складывать абстрактные цифры. Вы разберётесь в живых кейсах, например, поймёте, почему система Netflix из-за одного бага в коде внезапно "состарила" подростков до 127 лет.
Элиф, Тим, Трейси, Флэш, Зен - это не картонные гиды по курсу, а живые персонажи со своей историей и скрытыми мотивами. Они меняются от урока к уроку. Влюбляются. Ошибаются. Срываются на коллег. Исчезают и возвращаются. И к концу курса вы привяжетесь к ним так, что будете помнить их привычки лучше, чем конструкцию if-else.
Сам курс выстроен довольно просто, он подойдёт абсолютно всем, с любым уровнем знаний.
Если стало интересно, на курс можно записаться здесь.
P.S. Если дойдёте до финала - узнаете, что на самом деле случилось с Тимом. Но это уже отдельная история.😉
Ноябрь 2012 года, если не ошибаюсь. Может октябрь - уже не помню точно. Мне передали чужую программу т.к. человек который её писал уволился. Быстро уволился, без передачи дел. Программа собирала заявки от клиентов: имя, телефон, адрес доставки. Мне нужно было разобраться как она не пускает людей, которые вводят в поле "имя" какую-нибудь ерунду. Ну знаете - когда человек жмёт клавиши наугад или вводит пробелы. Нашёл нужное место в коде. И там было вот это:
^(?:[а-яА-ЯёЁ]{2,}[\s\-]){1,3}[а-яА-ЯёЁ]{2,}$
Я смотрел на это. Просто смотрел. Потом скопировал в гугл, что-то почитал - не особо помогло - закрыл вкладку. Написал тому кто это написал, хотя мы едва были знакомы: "слушай, а что вот это вообще?" Ответил через два дня: "а, это проверка ФИО, там всё понятно". Я написал "ясно, спасибо". Больше не спрашивал. Строчка работала и ладно. Но это меня беспокоило. Реально беспокоило, не для красного словца. Не люблю когда в коде есть что-то чужое и непонятное - особенно в том месте где пользователи вводят свои данные. Казалось что в любой момент что-то может пойти не так а я даже не пойму где смотреть.
Что это вообще такое?
Называется эта строчка регулярное выражение. Или регулярка. Некоторые говорят паттерн. Если совсем на пальцах - это способ описать форму текста а не конкретный текст. Не "найди слово Вася" а "найди любое слово из пяти букв которое начинается на В". Или "найди строку где сначала идут одиннадцать цифр подряд" - т.е. номер телефона, неважно как именно он записан. Вот с телефонами как раз хороший пример. Допустим вам прислали базу клиентов - пять тысяч строк. И в каждой строке телефон, но записан по-разному т.к. менеджеры вносили кто как хотел:
8-916-123-45-67 +7(916)1234567 89161234567 8 916 123 45 67
Это всё один и тот же номер. Вы это понимаете сразу. Компьютер - нет. Для него это четыре разные строки которые вообще не похожи друг на друга. Без регулярок тут либо руками, либо пишешь условие для каждого варианта. Потом находишь пятый вариант которого не учёл. Потом шестой. С регулярками - один шаблон, и всё.
Я с ними помирился года через три после той истории. Не потому что сел специально учить - просто задачи заставили. Разбирать логи сервера руками надоедает быстро. Чистить таблицы с кривыми данными вручную - тем более. Когда наконец разобрался - немного обидно стало. Там не так много всего. Символов которые реально нужны - штук пятнадцать, наверное. Большинство задач решаются вообще пятью-шестью. Точка означает "любой один символ". Звёздочка - "предыдущее встречается сколько угодно раз". Значок ^ в начале - "строка начинается здесь". $ в конце - "строка здесь заканчивается". \d - "любая цифра". Это не шифровка - это просто другой алфавит с другими значениями. Привыкаешь быстро.
Есть один момент который меня в своё время поставил в тупик. Регулярки бывают жадными. Это официальный термин, не придуманный. Когда говоришь "найди всё что между словом А и словом Б" - по умолчанию берётся максимум. Если слово Б встречается в тексте три раза - дотянется до последнего. Не до первого. Вот представьте строку с жалобами жильцов - всё в одну строку, так бывает в выгрузках:
жалоба: шум в подъезде жалоба: вода холодная жалоба: Олег с третьего
Хочешь найти что написано в первой жалобе. Ожидаешь получить "шум в подъезде". Получаешь всё от начала до последней "жалоба:", т.е. почти всю строку. Потому что жадно. Лечится одним символом - вопросительным знаком в нужном месте. Было "бери максимум" - стало "бери минимум, стоп при первом совпадении". Называется ленивый режим. Жадный и ленивый - два режима, один символ разницы. Я это понял не из учебника. Я это понял когда регулярка вернула мне половину лог-файла вместо одной строки которую я искал. Минут двадцать смотрел что не так.
В Питоне регулярки работают через модуль который называется re. Подключаешь одной строчкой и дальше либо ищешь все совпадения в тексте, либо заменяешь найденное на что-то другое, либо просто проверяешь есть такое в строке или нет. Выглядит это примерно так. Вот реальная ситуация которая случается часто: выгрузка из какой-то системы, строки с ошибками вперемешку с нормальными строками. Нужны только ошибки.
2024-01-15 ОШИБКА Иванова: дубликат записи
2024-01-16 ОК Петров: данные обновлены
2024-01-17 ОШИБКА Сидорова: неверный формат даты
2024-01-18 ОК Кузнецов: обновлено
2024-01-19 ОШИБКА Олег: снова что-то пошло не так
Для обработки пишу такой код
import re
errors = re.findall(r"^\d{4}-\d{2}-\d{2} ОШИБКА.*$", data, re.MULTILINE)
Читается примерно так: найди строки которые начинаются с даты - четыре цифры, дефис, две цифры, дефис, две цифры - потом идёт слово ОШИБКА, потом что угодно до конца строки. Всё остальное пропусти. Результат: три строки с ОШИБКА. Строки с ОК не попали. Одна строка вместо цикла с кучей условий.
Если вы никогда не видели Питон и непонятно что там написано - import re это просто "подключи инструмент для регулярок", без этого ничего не работает. re.findall - найди всё что подходит и верни списком. Остальное пока не важно.
Вернёмся к загадочной строчке из начала поста:
^(?:[а-яА-ЯёЁ]{2,}[\s\-]){1,3}[а-яА-ЯёЁ]{2,}$
Сейчас я читаю эту строчку примерно так: строка начинается и заканчивается кириллическими буквами, каждая часть минимум два символа, между частями пробел или дефис, таких частей от одной до трёх. Т.е. это проверка ФИО. Иванов Иван Иванович - пройдёт. "абракадабра" - нет. Пробелы - нет. Одна буква - нет. 3 минуты ступора тогда, 30 сек сейчас. Разница только в том что я теперь знаю что означают символы.
Я учил регулярки по кускам. Что-то из документации, что-то с Stack Overflow, что-то методом тыка - написал, не работает, поменял, заработало, не очень понял почему. Так и накапливалось. Это нормальный способ, я не жалуюсь. Но медленный. И каждый раз когда кто-то из коллег спрашивал "объясни как это работает" - я объяснял одно и то же. И каждый раз чувствовал что объясняю как-то криво. Не потому что сам не понимаю, а потому что под рукой не было нормального места куда можно было бы отправить человека. Есть доки - но они для тех кто уже знает. Есть статьи, но они на два часа, после которых всё равно идёшь в гугл.
Со временем я сделал курс. Провозился дольше, чем планировал, но зато сделал его так, как сам хотел бы проходить. Плюс справочники, я сам ими пользуюсь т.к. всё наизусть не помню. Не знаю подойдёт ли именно вам. Если просто хотели понять что такое регулярки и почему они выглядят как шифровка - надеюсь этот пост помог. Написал 72 урока и более 900 задач. 900, Карл! Теперь вижу регулярки во сне - это уже не лечится 🙃 Первые 7 уроков открыты, добро пожаловать в секту: Регулярные выражения (Regex) в Python: Интерактивный тренажер
И последнее. Вот строка из реальной почты, такое приходит, когда коллега пересылает что-то важное простым текстом:
Встреча с Ивановым перенесена. Новое время: 14:30.
Попробуйте написать шаблон, который вытащит из этой строки только время - «14:30». Две цифры, двоеточие, две цифры. Казалось бы, вроде бы просто, но интересно, как вы это запишете на языке регулярок. Пишите варианты в комментариях, позже разберём. Рабочих решений несколько, посмотрим, кто до каких доберётся 🙂
Создаём простую игру "Угадай число", в которой компьютер загадывает число, а игрок должен угадать его за наименьшее количество попыток.
Открываем любой редактор кода, например, online-python.com. Для создания игры мы будем использовать модуль random в Python. Модуль random генерит случайное число, которое будет загадано компьютером. Вот как будет выглядеть наш код для загадывания числа:
В этом коде мы импортируем модуль random и метод randint(), чтобы сгенерировать случайное число от 1 до 100.
Далее пишем код для получения ответа игрока и его проверки. Для этого мы будем использовать цикл while, который будет выполняться до тех пор, пока игрок не угадает верное число:
Сначала мы запрашиваем ответ игрока с помощью функции input(). Затем мы запускаем цикл while. Если ответ игрока неверный, мы выводим подсказку и просим ввести число еще раз. Если ответ игрока правильный, мы выводим сообщение об этом и завершаем игру.
Теперь мы можем запустить нашу игру и начать играть! Вот что должно получиться:
Как видите, ничего особо сложного нет, а для того, чтобы самому написать такую программу, достаточно изучить основы языка. Список годных материалов я на писал в посте поста «Как я стал Python-разработчиком» :)
