Сообщество - Программирование на python

Программирование на python

1 020 постов 11 965 подписчиков

Популярные теги в сообществе:

6

Освоение разделения PDF в Python: от отдельных страниц до извлечения произвольных диапазонов

Освоение разделения PDF в Python: от отдельных страниц до извлечения произвольных диапазонов

Сталкивались ли вы когда-нибудь с такой ситуацией: вы получаете PDF-отчёт на сотни страниц, пытаетесь переслать его коллеге, но вложение в письме превышает допустимый размер и отклоняется? Или вам нужна только конкретная глава, но приходится вручную извлекать её постранично из сотен страниц? Эти «громоздкие» файлы всегда доставляют головную боль при обмене, печати или архивировании. Решение на самом деле довольно простое — просто разделите PDF на несколько меньших файлов по требованию, и проблема решена.

В этой статье мы покажем, как использовать библиотеку Spire.PDF for Python для реализации двух основных режимов разделения с минимальным количеством кода: разделение на одностраничные PDF-файлы одним нажатием и гибкое разделение по произвольным диапазонам страниц .

Предварительные требования: настройка окружения и импорт библиотеки

Spire.PDF for Python — это профессиональный компонент для работы с PDF, который работает полностью независимо от внешних программ, таких как Adobe Acrobat, что делает его идеальным для интеграции в серверы или автоматизированные скрипты.

Команда установки:

pip install Spire.PDF

После установки импортируйте необходимые модули в начале файла с кодом, чтобы приступить к работе.

Режим 1: Разделение каждой страницы на отдельные одностраничные PDF-файлы

Если ваша бизнес-задача требует сохранить каждую страницу многодесятистраничного контракта, тендерной документации или электронной книги как отдельный PDF-файл, метод Split() является самым удобным выбором.

from spire.pdf.common import *

from spire.pdf import *

# 1. Инициализация и загрузка исходного PDF-документа

doc = PdfDocument()

doc.LoadFromFile("Sample.pdf")

# 2. Разделение: сохранение каждой страницы как независимого одностраничного PDF-файла

# {0} — это заполнитель номера страницы; второй параметр 1 указывает, что нумерация начинается с 1

doc.Split("Output/SplitDocument-{0}.pdf", 1)

# 3. Освобождение ресурсов

doc.Close()

Пояснение метода:

Полная сигнатура метода Split() — Split(string fileName, int startNumber), и его функция заключается в строго фиксированном разделении каждой страницы PDF на независимый одностраничный PDF-файл — это поведение нельзя изменить с помощью параметров.

Два параметра означают следующее:

  • fileName : Путь для выходного файла и шаблон имени. {0} внутри является заполнителем, который будет автоматически заменён фактическим номером страницы при сохранении.

  • startNumber : Задаёт начальный номер для заполнителя {0}. Он влияет только на нумерацию в имени файла и не имеет отношения к логике разделения .

Режим 2: Разделение по требованию путём выбора диапазонов страниц

Иногда нам не нужно разделять каждую страницу, а хочется разбить PDF на логические части. Например: Часть 1 (обложка) , Часть 2 (основной текст, главы 1–3) , Часть 3 (приложение) .

В этом случае мы можем создать несколько объектов PdfDocument и использовать метод InsertPageRange() для точного извлечения определённых страниц из исходного документа.

from spire.pdf.common import *

from spire.pdf import *

# Загрузка исходного документа

doc = PdfDocument()

doc.LoadFromFile("Sample.pdf")

# Создание трёх пустых PDF-объектов для хранения разделённого содержимого

newDoc_1 = PdfDocument()

newDoc_2 = PdfDocument()

newDoc_3 = PdfDocument()

# 1. Извлечение страницы 1 (обложка)

# Примечание: индексы страниц начинаются с 0, поэтому страница 1 соответствует индексу 0

newDoc_1.InsertPage(doc, 0)

# 2. Извлечение страниц со 2 по 4 (начало основного текста)

# Параметры InsertPageRange: (исходный_документ, начальный_индекс, конечный_индекс)

newDoc_2.InsertPageRange(doc, 1, 3)

# 3. Извлечение страниц с 5 по последнюю (оставшаяся часть)

# doc.Pages.Count получает общее количество страниц; вычитание 1 даёт индекс последней страницы

newDoc_3.InsertPageRange(doc, 4, doc.Pages.Count - 1)

# Сохранение трёх разделённых документов по отдельности

newDoc_1.SaveToFile("Output1/Split-1.pdf")

newDoc_2.SaveToFile("Output1/Split-2.pdf")

newDoc_3.SaveToFile("Output1/Split-3.pdf")

# Закрытие всех объектов документа для освобождения памяти

doc.Close()

newDoc_1.Close()

newDoc_2.Close()

newDoc_3.Close()

Правила индексации страниц (очень важно):

В Spire.PDF нумерация страниц следует общепринятому в программировании соглашению — начиная с 0 . То есть doc.Pages[0] представляет первую страницу PDF. Понимание этого критически важно для точного извлечения диапазонов:

  • InsertPage(doc, 0): вставляет страницу 1.

  • InsertPageRange(doc, 1, 3): вставляет страницы со 2 по 4 включительно.

  • InsertPageRange(doc, 4, doc.Pages.Count - 1): начинается со страницы 5 и до физической последней страницы.

Преимущество такого подхода к разделению — его исключительная гибкость: вы можете комбинировать страницы по любым правилам или даже извлекать страницы из разных исходных файлов и объединять их в новый PDF (используя возможность InsertPage работать с разными документами).

Расширение: Разделение по фиксированным группам страниц

Стоит отметить, что метод Split() не может выполнить требование «объединять каждые N страниц в один PDF» (например, объединять каждые 2 страницы в один файл). Если вам требуется такое групповое разделение, необходимо использовать InsertPageRange в цикле для ручной реализации:

# Пример: объединение каждых 2 страниц в один PDF

group_size = 2

for i in range(0, doc.Pages.Count, group_size):

new_doc = PdfDocument()

end_index = min(i + group_size - 1, doc.Pages.Count - 1)

new_doc.InsertPageRange(doc, i, end_index)

new_doc.SaveToFile(f"Output/Group-{i // group_size + 1}.pdf")

new_doc.Close()

Рекомендации по выбору между двумя режимами

Освоение разделения PDF в Python: от отдельных страниц до извлечения произвольных диапазонов

Заключение

Как показано в приведённых примерах, с помощью Spire.PDF for Python мы можем выполнять сложные задачи по разделению PDF, используя всего лишь чуть более десятка строк основного кода. Будь то пакетная обработка архивных файлов или интеграция функциональности управления документами в систему, это решение может значительно сэкономить время разработки.

Надеемся, эта статья поможет вам эффективно решать задачи по разделению PDF! Если вы столкнётесь с другими проблемами на практике, не стесняйтесь изучать их дальше.

Показать полностью 2
9

100 дней изучения Python. День 1. Меня зовут Элиф, и я понятия не имею, что делаю

Серия Дневник Элиф Дебажной

UPD:

Дисклеймер: Элиф Дебажная - вымышленный персонаж. Посты написаны от её лица как дневник обучения Python. Это художественный формат, цель которого - показать путь новичка со всеми ошибками, вопросами и открытиями.

Комментарии от имени Элиф в этой серии также являются частью художественного формата. Она отвечает читателям как персонаж дневника, а не как реальный человек. Все обсуждения, реакции и самоирония нужны для сохранения атмосферы истории.

Привет, Пикабу. Меня зовут Элиф, мне 26.

Решила устроить себе небольшой эксперимент и заодно проверить, насколько у меня крепкая психика.

Следующие 100 дней буду изучать Python и выкладывать сюда всё как есть. Без красивых историй про "успех за две недели". Если код сломается, то покажу. Если три часа буду искать пропущенную запятую - тоже покажу. Если случайно удалю половину проекта... надеюсь, не покажу. Хотя кого я обманываю.

До IT я работала в другой сфере, а программирование для меня долго было чем-то вроде магии: люди нажимают на клавиши, потом покупают квартиру.

В какой-то момент решила перестать смотреть на это со стороны и попробовать сама.

Что будет в этом дневнике:

  • самые глупые ошибки, которые только можно сделать;

  • мемы про Python;

  • разборы того, что наконец удалось понять.

Пока мои достижения выглядят так:

  1. установила Python;

  2. установила VS Code;

  3. случайно открыла терминал;

  4. испугалась.

Не обещаю стать сеньором за три месяца. Но обещаю честно рассказывать обо всех победах и фейлах.

Если кому-то будет смешно - отлично.

Если кому-то пригодится мой опыт - ещё лучше.

Если опытные разработчики будут иногда пинать в нужную сторону - вообще замечательно.

День 1 начинается. Пожелайте удачи. Кажется, дальше будут отступы.

P.S. Если через неделю увидите пост "Перешла разводить суккуленты", значит Python оказался сильнее.

100 дней изучения Python. День 1. Меня зовут Элиф, и я понятия не имею, что делаю
Показать полностью 1
0

Как я устал от алгоритмов соцсетей и написал своего ИИ-ассистента для чтения новостей (и почему RSS всё ещё жив)

Привет, Пикабу!

Я — независимый разработчик. И, как и многие из вас, я поймал себя на ужасной зависимости: каждое утро я открывал Telegram, Хабр, новостные сайты и кучу каналов, тратя по 2 часа на бесконечный скроллинг. Алгоритмы соцсетей созданы для того, чтобы жрать наше время, подсовывая кликбейты вместо реально важной информации. В итоге к полудню голова уже была забита информационным мусором, а фокус полностью терялся.

Я вспомнил старый добрый RSS. Идея отличная: ты сам выбираешь, что читать, без умных лент и рекомендаций. Но читать 100+ полных статей в день вручную — это тоже адски долго.

Тогда я решил сделать инструмент для себя — карманного ИИ-ассистента прямо в Telegram, который взял бы на себя всю рутину. И вот что из этого вышло.

Что умеет мой бот?

  1. Вытаскивать суть (AI Digest): Бот сам заходит по ссылке (даже если там пейволл или сайт перегружен скриптами), выкачивает чистый текст, анализирует его с помощью ИИ и выдает мне сжатую суть в виде лаконичного дайджеста. Чтение статьи на 15 минут теперь занимает 30 секунд.

  2. Умный слайдер: Если у вас мало времени, бот генерирует короткие слайды-выжимки по ключевым темам.

  3. Карманный подкаст (Аудиофичи): Моя любимая фишка. Бот умеет озвучивать дайджесты голосом. Я просто нажимаю кнопку, вставляю наушники и слушаю выжимку утренних новостей, пока варю кофе или иду на прогулку.

  4. Никакой рекламы и мусора: Никаких баннеров, всплывающих окон и кликбейтов.

Как это устроено внутри?

Архитектура довольно простая, но отказоустойчивая. Бот написан на Python (библиотека Aiogram 3). В качестве базы данных используется PostgreSQL для хранения пользователей и подписок, а быстрый кэш и стейты (FSM) крутятся на Redis. Парсингом и очисткой страниц от мусора занимается кастомный инстанс Mercury Parser API, а суммаризацией и генерацией аудио — легковесные и быстрые ИИ-модели.

Поскольку я считаю, что информационная перегрузка — это глобальная боль, я перевел интерфейс бота на четыре языка: русский, английский, испанский и персидский.

Для чего я это пишу?

Бот полностью бесплатный для старта (мне действительно важно собрать фидбэк от реальных людей, а не заработать все деньги мира). Я хочу сделать инструмент, которым будет удобно пользоваться каждому, кто ценит свое время и ментальное здоровье.

Буду очень рад, если вы потестируете бота, найдете баги (а они наверняка есть, ведь я пилил его по ночам!) и напишете в комментариях, каких фич вам не хватает.

Ссылка на бота: @RSS_READER_AI_Bot

Берегите свой фокус и не позволяйте алгоритмам решать за вас, что сегодня читать!

Показать полностью
52

Ответ Ghost687 в «Конец реверс-инжинирингу: релиз Markov Shield AI — софт, который "намертво" привязывает код к железу»2

Серия Простыми словами
Это не допилили малость "защитнички".

Это не допилили малость "защитнички".

Просто разрабы неправильно поняли суть проблемы и решили на отвяжись поставленную задачу.

Если кто не понял исходный пост, то это такая своеобразная защита методом вынесения какой-то функции из читабельного или уязвимого к анализу софта в черный ящик, который трудно реверсинженерить.

Правильный способ сделать такую защиту трудновзамываемой есть, и тут вспоминается один из проектов моей далёкой бурной разработческой молодости.

Меня как-то озадачили необходимостью защиты некоторого инженерного софта (написанного, кстати, мною же) от нелицензионного использования. Важно было, чтобы у лицензиата не было физической возможности обеспечить нашим софтом больше рабочих мест, чем те, за которые было уплочено.

После разговора с потенциальными клиентами мы сразу решили, что онлайн защиты нам не подходят. Ну, типа, если у клиента ляжет интернет по какой-то причине, он не сможет обслужить своих клиентов, кои обычно богатые деньгами. но небогатые временем серьёзные люди. Все наши клиенты не хотели, чтобы софт зависел от интернета.

В связи со всем вышеперечисленным выбор наш пал на аппаратные ключи, которые в клиентский комп вставляются в USB-порт и без которых наша защищаемая CAD-система превращается в тыкву.

Вот такие вот ключики мы использовали

Вот такие вот ключики мы использовали

Внутри у него своего рода маленький компьютер, примерно такой же находится в чипах банковских карт и RFID-карт доступа. Фактически там микроконтроллер, на котором запущена маленькая ява-машина, выполняющая код, который в устройство запищет разработчик.

Достать записанный в ключ код - задача настолько сложная, что трудно окупить таким образом стоимость взлома этого донгла безлимитными лицензиями на что угодно.

Правильный способ использования защиты ПО на основе таких ключей заключался в том, чтобы вынести на этот ключ кусочки секретных алгоритмов, которые обычно размещены прямо в бинарниках защищаемой программы.

Это очень не просто - выбрать такие чистые (в функциональном смысле). компактные (в донгле не так много места), не критичные к скорости (ява-машина в ключике не впечатляла скоростью, а всё ещё и через USB2.0 работало... если не 1.1, не помню уже), нетривиальные функции, которые нельзя было бы повторить так просто в эмуляторе ключа.

Да, можно было так же наговнякать как в исходном посте, вернуть true и проверять потом, но такие ключи - просто деньги на ветер.

По-моему, я выносил в ключи функции поиска и фильтрации граней на многограннике для определения поверхностей, подлежащих обработке. Алгоритм самописный, не тривиальный, достаточно быстро работал на ядре этой финтифлюжки, не требовавший объёмных аргументов и т.д.

Навернр в эмуляторе ключа можно было что-то изобрести похожее, но тогда (конец двухтысячных) не было ИИшек, которые делают сейчас такой реверс-инженеринг достаточно дешевым, а наш софт был не таким распространённым и известным, чтобы на его взлом тратить дохрена денег и мозгов грамотных специалистов.

И да, надо понимать, что любая просто быстро и наивно реализованная защита легко и дёшево обходится. Главное, чтобы это было экономически выгодно, а за взломом дело не встанет.

Вот и тут, не так просто найти даже в большом инженерном приложении кусочка, который можно настолько чисто изолировать, чтобы он мог работать на отдельном процессоре. А ещё надо, чтобы разработка и исправление ошибок в рамках поддержки защищенного ПО не превратились в кошмар, чтобы неаккуратность или ошибка разработчиков не поломали законно купленные лицензии.

И вот тут я расскажу ещё про один лайфхак. Защита должна быть многослойная и вероятностная.

Если ваш защитный механизм мгновенно замечает факт нелицензионного использования, и тут же блокирует работу защищаемого софта, то это плохой защитный механизм.

Хороший механизм будет некоторое время накапливать признаки нелицензионного использования, порог блокировки будет рандомизирован таким образом, чтобы нельзя было "нащупать" коридор допустимого при обходе защиты.

В нашем случае если приложение не видело аппаратный ключ, оно рисовало поверх рабочей области простой сплеш-скрин с просьбой его подключить. Технически это никакая не защита. потому что требуется элементарное вмешательство на уровне API Windows, чтобы убрать помеху и работать дальше. Но после этого софт переходит в режим войны.

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

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

Эти рандомизации критериев и отложенные реакции позволяют усложнить и замедлить цикл подбора параметров защищенных функций при взломе.

Сейчас такая защита тоже н еимеет никакого смысла, потому что ничего, в общем-то, не стоит переписать практически любой такой софт с нуля по образу и подобию готового прототипа с помощью нейронки. А раньше эта защита была неимоверно передовой и непробиваемой.

В геймдеве, кстати, её не использовали обычно скорее всего из-за медленной работы и трудности вынесения чувствительных и достаточно нетривиальных алгоритмов на аппаратный ключ.

Ну вот такой вот кусочек совершенно ненужного теперь опыта. Поделился с подписчиками.

Если непонятно что-то объяснил - спрашивайте. Мне казалось я умею объяснять сложные вещи простыми словами, особенно если сам понимаю эти сложные вещи. Но и на старуху, как говорится...

Типовой дисклеймер, которыми я снабжаю все свои посты.
Этот текст написан биогенным естественным интеллектом, работающим на аппаратном обеспечении моего собственного головного мозга. Никаких методов написания или улучшения текста генеративными моделями не применялось.
В наше время такое встречается не часто и я такое ценю, потому и добавляю такой дисклеймер.

Показать полностью 1

"Я приду в синем костюме": как ИИ от Anthropic словил шизу и вообразил себя человеком

Редакция 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. Если дойдёте до финала - узнаете, что на самом деле случилось с Тимом. Но это уже отдельная история.😉

Показать полностью 3
2

TG Notion продолжение...

Серия TG Notion
TG Notion продолжение...

Сегодня я зашел в TG Notion с одной целью — прикрутить общие доски. Чтобы любой человек мог кинуть ссылку в чат, и там сразу открывалась совместная заметка. Без регистраций, без логинов, без этого ебучего «дай доступ» в Notion.

План был простой:

  1. Сделать API для создания досок.

  2. Сделать API для работы с заметками внутри доски.

  3. Сделать фронт, который это все открывает.

Ага, план был простой.

Первое, что я понял: BotHost — это зло. Не потому что он плохой, а потому что у него своя логика работы с базой данных. Ты делаешь пуш, а твоя локальная SQLite сгорает нахуй вместе со всеми тестовыми досками. Потому что база лежала внутри контейнера, а при деплое контейнер пересоздается.

Я чуть не разбил монитор.

Дальше — роутинг. Ты думаешь, что если написать /boards/:hash, то сервер отдаст страницу. А он отдает JSON. Потому что сверху сработал API-роут. Ты переписываешь — он отдает 404.

И тут начинается коллективный интеллект.

В чат залетает Vijay Taitoo ☭ (в миру — Никита). И он тупо видит то, что я сам не видел, потому что уже замылил глаз.

«Ты сначала стабилизируй базу, потом уже строй этажи.»
«Там проблема не в доске, а в том, что роут страницы и роут API дерутся между собой.»
«Если ссылка открывается в браузере — у тебя фронт ищет стили как /boards/style.css, а надо /style.css.»

Он не писал за меня код. Он просто смотрел со стороны. Это называется — КИ (Коллективный Интеллект). В отличие от ИИ, который выдает идеальный код, но молчит, когда ты загоняешь себя в тупик.

Потому что ИИ — это инструмент. А живой человек видит полотно целиком.

После двух часов плясок с req.url, pathname и fs.readFile, я переписал API и фронт так, чтобы доски открывались внутри Mini App, а ссылка выглядела как:

https://tgnotion.bothost.tech/boards/333a6575

И знаете что? Она заработала.

Когда я кинул ссылку в чат, Никита зашел, написал «Doors», сохранил — и заметка тут же появилась у меня. Это была не радость. Это было удовлетворение от того, что система не сломалась.

Сейчас доска умеет:

  • 📋 Создавать общую доску.

  • ✏️ Добавлять заметки (видит кто добавил).

  • 🔗 Делиться ссылкой (при создании).

  • 🎨 Работает внутри Telegram Mini App, а не как тупая страница.

Остались мелочи:

  • Кнопка удалить/редактировать.

  • Автообновление (чтобы видеть изменения в реальном времени).

  • Поиск по доске.

  • Предпросмотр ссылки (чтобы было видно название доски, а не хеш).

Но это уже завтра. Сейчас главное — фундамент стоит, доски работают, и я могу спать спокойно.

TG Notion — https://t.me/Telega_notion_bot
КАНАЛ

Показать полностью 1
4

Устал открывать Notion. Сделал всё в Telegram

Серия TG Notion

Привет Пикабу. Сижу в Telegram круглосуточно — работа, созвоны, проекты, всё там. И вечно надо что-то записать: идею, задачу, список покупок. А для этого раньше приходилось открывать Notion, искать блоки, разбираться в интерфейсе. Бесит.

Я подумал: а нахуя мне этот Notion, если я всё равно в Telegram? И просто сделал своего бота. Пока что он умеет только две вещи:
— записывать заметки;
— вести список задач.

Всё. Никаких тебе папок, тегов, иерархий, настроек. Написал — сохранил. Всё просто и быстро.

И знаете что? Я буквально вчера его задеплоил, а уже 70 человек сидят и тестируют. Ни копейки не потратил на рекламу — просто кинул пост на Хабре. Люди заходят, пробуют и пишут мне в личку: "Слушай, а добавь сюда общие доски, чтобы командой работать?" Или: "А можно будет делиться заметками с другом?".

И я реально прислушиваюсь. Потому что я не хочу делать продукт, который никому не нужен. У меня есть канал, где мы голосуем, что добавить следующим. И новые фичи я делаю только если людям это реально надо.

Дальше в планах:
— общие доски (тодолист для команды);
— шеринг заметок (делиться карточкой в чат);
— напоминалки (чтобы бот сам напоминал о дедлайнах);
— интеграция с Notion и Google Docs (чтобы можно было экспортировать);
— ну и английский язык, конечно.

Пока что примитивно, но я верю в подход "от простого к сложному". Сначала сделал базовую хуйню, теперь смотрю, что просят люди, и доращиваю.

Если тебе интересно — вот бот, попробуй:
https://t.me/Telega_notion_bot

А вот мой GitHub:
https://github.com/Elrick-from-Malnibone/tg-notion-node

Кто попробует — пиши в комменты, что добавить следующим. Я реально читаю.

Какую фичу запилить следующей?
Всего голосов:
Показать полностью 1
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества