Это адаптированный перевод статьи Джереми Ховарда (Jeremy Howard) nbdev: use Jupyter Notebooks for everything. Повествование ведётся от лица автора.
Крис Латтнер, создатель Swift, LLVM и Swift Playgrounds: «Я действительно считаю, что nbdev — огромный шаг вперёд для программного окружения».
Я и мой коллега по fast.ai Сильвен Гаггер (Sylvain Gugger) последние несколько лет работали над любимым проектом. Это программное окружение Python, которое называется nbdev. Это окружение позволяет создавать полноценные пакеты Python, включая тесты и систему документации, в Jupyter Notebook. Мы уже написали библиотеку fastai с помощью nbdev. Также сделали с помощью этого инструмента ещё несколько небольших проектов.
Nbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они себя ведут. Мы исследуем поведение алгоритма, который разрабатываем, чтобы понять, как он работает с разными видами данных. Мы отлаживаем код, пробуя разные комбинации входящих данных, и так далее.
Содержание
Nbdev: исследовательское программирование
Мы верим, что каждое исследование имеет ценность само по себе, и что результаты каждого исследования надо сохранять для других программистов, включая самого исследователя через полгода. Думайте об этом как о дневнике исследователя. Вы можете использовать его, чтобы показать, что вы попробовали, что сработало, а что нет, и что вы сделали, чтобы в конце концов понять систему, с которой работаете. В процессе исследования вы понимаете, что какие-то части понимания критически важны для работы системы. Поэтому исследование должно включать тесты и утверждения (assertions) для закрепления такого поведения.
Такой способ исследования просто организовать, когда вы работаете в REPL или в среде типа Jupyter Notebook. Но эти системы не так хороши для программирования. Поэтому специалисты чаще используют такие системы для ранних исследований, а потом переключаются на текстовый редактор или IDE. Они переключаются, чтобы получить дополнительные возможности, например, подсветку синтаксиса, интеграцию с юнит-тестами, и, самое важное, возможность создать итоговые файлы с исходным кодом, а не код в блокноте или REPL.
Nbdev переносит ключевые преимущества IDE в систему ноутбуков. То есть вы можете работать в ноутбуке без ущерба для удобства и результата.
Чтобы достичь нужной интерактивности, nbdev использует Jupyter Notebook в качестве платформы. Это позволяет по максимуму раскрыть динамические свойства языка Python, но привносит и другие инструменты, жизненно необходимые разработчику ПО:
- Автоматическое создание модулей Python, в которых учтены лучшие практики, например, автоматическое определение __all__ с экспортом функций, классов и переменных.
- Навигация и редактирование кода в стандартном редакторе или IDE, и автоматический экспорт изменений в ноутбук.
- Автоматическое создание индексируемой перелинкованной документации. Любое слово, обёрнутое в бэктики, автоматически линкуется с соответствующим разделом документации. В разделе документации создаётся сайдбар, в котором находятся ссылки на модули.
- Установщики pip, загруженные в pypi.
- Тесты, объявленные прямо в блокноте и запускаемые в фоне.
- Continuous integration (CI).
- Разрешение конфликтов при версионировании кода.
Вот сниппет из нашего актуального исходного кода nbdev, который, конечно же, написан в nbdev.
Изучаем формат файлов в исходном коде nbdev
Как видно, когда вы пишете программы в таком ключе, каждый человек в команде получает пользу от вашей работы. Поскольку вы разрабатываете в ноутбуке, можно также добавлять графики, текст, ссылки, фото, видео и так далее. Этот контент автоматически попадает в документацию. Ячейки, в которых определён код, скрываются и заменяются стандартизированной документацией для вашей функции. В документации отображается имя, аргументы, строки документации, ссылка на исходный код на GitHub.
В документации nbdev вы найдёте больше информации о функциональности, установке и способах использования этого инструмента. Как вы помните, эта документация генерируется автоматически. Далее в этой статье я описываю историю и контекст «почему»: почему мы создали инструмент, и почему мы сделали его именно таким.
Инструменты для разработки программного обеспечения
Большинство инструментов для разработчиков создано без учёта особенностей исследовательского программирования. Когда я начинал программировать около 30 лет назад, практически повсеместно использовалась каскадная модель (на сленге её иногда называют «модель водопадов»). Мне казалось, что эта модель, предполагающая точную детализацию и максимально близкое следование документации, не соответствовала тому, как я на самом деле работаю.
В 90-х годах ситуация изменилась. Популярным стал подход agile. Люди стали понимать, что разработка — итеративный процесс. Появились подходы, которые учитывают это. Однако инструменты разработки существенно не поменялись. В нашем арсенале появились инструменты, которые подходят для TDD (разработка через тестирование). Однако такие инструменты внедрялись скорее как расширения для существующих редакторов, чем как полностью переработанные среды разработки.
В последние годы мы видим рост интереса к исследовательскому тестированию как к инструменту аджайл-подхода. Это нам интересно, но мы считаем, что этот подход развивается недостаточно глубоко. Мы считаем, что исследования должны быть центральной частью разработки практически всегда.
Легендарный Дональд Кнут опередил своё время. В 1983 году он представил концепцию грамотного программирования. Кнут описал её как «методологию, которая сочетает использование языка программирования и языка документирования. Она делает программы более простыми и надёжными. Также методология упрощает разработку программ, написанных на высокоуровневых языках. Главная идея — рассматривать программы как произведения литературы, которые создаются для людей, а не для компьютеров». Я долго был очарован этой идеей, но она, к сожалению, не получила широкого распространения. Инструменты, созданные для такого подхода, замедляли разработку. Это подошло далеко не всем специалистам.
Спустя примерно 30 лет после появления концепции грамотного программирования ещё один яркий разработчик — Брет Виктор — сформулировал претензии к существующим инструментам для программистов. Он рассказал, как создавать системы программирования для понимания программ. В знаменитой речи «Принципы изобретательства» Брет Виктор отметил: «В рамках существующей концепции компьютерные программы рассматриваются как список текстовых определений, которые мы передаём в компилятор. Этот подход родом из 50-х годов прошлого века. Тогда в ходу были Fortran и ALGOL — языки, созданные для работы с перфокартами».
Брет Виктор сформулировал и проиллюстрировал примеры и принципы нового подхода к созданию систем для программирования. Пока никто не реализовал все идеи Виктора. Но попытки частичной имплементации были. Одна из самых известных — Swift Playgrounds Криса Латтнера.
Демонстрация Playgrounds в Xcode
Это большой шаг вперёд, но здесь всё ещё есть явные ограничения среды, которая создавалась без учёта необходимости исследований. Например, сам процесс исследования здесь вообще не отражается. Тесты тоже не интегрированы. То есть полного внедрения принципов грамотного программирования нет.
Интерактивные среды программирования
Ещё одно направление развития систем программирования — интерактивные среды разработки. Они появились несколько десятилетий назад вместе с LISP и REPL, которые позволили разработчикам интерактивно редактировать код в работающем приложении. Smalltalk пошёл ещё дальше, создав интерактивное визуальное рабочее пространство. Во всех этих случаях языки программирования подходили для интерактивной работы. Например, LISP с его макросистемой и кодом как данными вписался в эту модель.
Интерактивное программирование в Smalltalk
Хотя этот подход нельзя назвать общепринятым, он стал популярным в научной сфере, статистике и других областях Data Driven Development ( программирования, управляемого данными). Фронтенд-программирование на JavaScript тоже позаимствовало важные концепции из этого подхода, например, горячую перезагрузку или интерактивное редактирование кода в браузере.
MATLAB тоже создавался как полностью интерактивный инструмент в 70-е годы. Он и сейчас используется в инженерии, биологии и других отраслях. Похожие принципы используются в SPLUS и R, которые широко используются в области статистики и визуализации данных.
Я был приятно удивлён, когда впервые использовал систему Mathematica около 25 лет назад. Эта система показалась мне наиболее приближенной к грамотному программированию. Её применение не влияло на продуктивность. В Mathematica используется интерфейс ноутбуков, который ведёт себя практически как REPL. Также система позволяет использовать графики, изображения, форматированный текст и так далее. Эта система позволила мне делать то, что раньше было недоступным. Например, она позволяет тестировать алгоритмы и получать визуальную обратную связь.
Но Mathematica не позволила мне сделать что-то полезное, так как с её помощью невозможно поделиться кодом или приложениями с коллегами, пока вы не оплатите дорогую лицензию. Также Mathematica не позволяет делать приложения, которые можно использовать в браузере. Кроме того, мне показалось, что код Mathematica более медленный и требовательный к памяти, чем код, написанный на других языках.
Можете представить мою радость, когда я узнал о выходе Jupyter Notebook. В нём используется такой же простой интерфейс ноутбуков, как в Mathematica. Но это продукт с открытым исходным кодом, что позволило мне писать на доступных языках с широкой поддержкой. Я использовал Jupyter Notebook не только для изучения алгоритмов, API и проверки новых идей. Этот инструмент стал средством обучения в fast.ai. Многие студенты убедились, что возможность экспериментировать с входящими данными и сразу же видеть результат позволяет лучше усваивать учебный материал.
Мы также написали книгу, используя только Jupyter Notebook. Этот инструмент позволяет без затруднений комбинировать текст, примеры кода, иерархически структурированные заголовки. И он гарантирует, что на выходе наш контент выглядит так, как мы ожидаем.
Одним словом, нам и нашим студентам понравился Jupyter Notebook. Оставалось использовать его для разработки нашего программного обеспечения.
Чего не хватает в Jupyter Notebook
Jupyter Notebook хорош для исследовательской части исследовательского программирования, но не так хорош для собственно программирования. Например, в нём невозможно:
- Создавать модульный код, который можно переиспользовать и запускать за пределами Jupyter Notebook.
- Создавать гипертекстовую индексируемую документацию.
- Тестировать код, включая автоматическое тестирование при непрерывной интеграции.
- Удобно перемещаться по коду.
- Контролировать версии.
Из-за таких проблем люди вынуждены переключаться между инструментами, например, использовать IDE или редакторы, REPL/shell, блокноты и так далее.
Мы решили, что лучший способ справиться с проблемой — использовать хорошие существующие инструменты, где это возможно, и создавать собственные инструменты, если это необходимо. Например, для управления пулреквестами и отслеживания диффов есть отличный инструмент — ReviewNB. Когда вы смотрите на визуальное представление диффов в ReviewNB, понимаете, насколько ограничены стандартные текстовые представления диффов. Например, как можно понять по тексту, что в коммите есть некачественное изображение? Или что в графиках не хватает меток? А благодаря визуальному представлению диффа вы знаете, что происходит.
Визуальное представление диффа в ReviewNB
Nbdev предупреждает большинство конфликтов при слиянии, так как в инструменте используются хуки, которые автоматически удаляют метаданные, вызывающие большую часть таких конфликтов. Если у вас появляется конфликт при синхронизации репозитория, достаточно запустить nbdev_fix_merge. В этом случае nbdev использует ваши исходящие данные, если есть конфликты исходящих данных. Если есть конфликты входящих данных, nbdev включает все данные в итоговый документ и указывает на конфликт. Благодаря этому вы можете быстро найти и исправить проблему.
Конфликт при слиянии отображён в nbdev
Чтобы получить модульный переиспользуемый код в nbdev, достаточно создать модули Python. Nbdev ищет в коде специальные комментарии, например, #export. Такие комментарии сигнализируют, что ячейку нужно экспортировать в модуль Python. Каждый блокнот связывается с конкретным модулем с помощью специального комментария. Сайт с документацией автоматически создаётся с помощью генератора статических сайтов Jekyll. Такие сайты можно размещать на GitHub Pages. Мы написали свою систему документации, так как существующие решения, например, Sphinx, не давали нам все необходимые возможности.
В большинстве редакторов и IDE есть хорошие инструменты для навигации по коду. А GitHub поддерживает навигацию по коду в веб-интерфейсе. Поэтому мы обеспечили возможность навигации по коду, который экспортируется из nbdev. Более того, если вы редактируете код в IDE или в веб-интерфейсе, изменения автоматически синхронизируются с nbdev.
Для тестирования мы написали собственную простую библиотеку и утилиту командной строки. Тесты можно писать непосредственно в ноутбуках, это часть исследования и документирования. Естественная statefulness ноутбуков (наличие у них состояния) позволяет использовать как юнит-тесты, так и интеграционные тесты. Вместо изучения нового син��аксиса для создания тестов вы можете использовать коллекции и циклы Python. Созданные тесты можно запускать в среде Continuous Integration (CI), которую вы используете. При этом вы будете получать информацию об источниках ошибок. Nbdev интегрирован с GitHub Actions. Пулреквесты для интеграции с другими платформами приветствуются.
Динамический Python
Одна из проблем с поддержкой Python в IDE или редакторе связана с динамическими свойствами Python. Например, вы в любой момент можете добавить метод в класс, с помощью метаклассов изменить способы создания классов, а с помощью декораторов изменять поведение функций и методов. Microsoft создал Language Server Protocol, который может использоваться в средах разработки для получения информации о текущем файле. Использование этого протокола необходимо для важных функций, например, автодополнений, навигации по коду и так далее. Однако при работе с динамическими языками, такими, как Python, подобная информация всегда будет носить характер догадок, так как для получения актуальных данных надо запускать код. Это не всегда возможно по целому ряду причин.
С другой стороны, в ноутбуках есть интерпретатор Python, который вы полностью контролируете. Таким образом Jupyter поддерживает автодополнения, списки параметров, контекстную документацию, которая основана на актуальном состоянии кода.
Что дальше
Одновременно с разработкой nbdev мы писали вторую версию fastai полностью в nbdev. В fastai есть структурированное API для создания моделей глубинного обучения (deep learning). Релиз планируется в первой половине 2020 года. Этот продукт уже наполнен функциональностью, и первые пользователи уже создают классные проекты с помощью тестовой версии.
Мы заметили рост продуктивности в два или три раза при использовании nbdev по сравнению с другими инструментами. Я программирую около 30 лет практически каждый день, и такой рост продуктивности меня приятно удивил.
Спасибо за интерес к nbdev.
Адаптированный перевод статьи nbdev: use Jupyter Notebooks for everything by Jeremy Howard.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 20:52:45 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="2Q3D7dPjlgELYOzWma3M6rOjemwO8ZvIu6jeVifdM5c23AjaIZ07Yb0jyE6Vojydc6pXxgbGZWoGSEQCddrU-Q";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Nbdev: используйте Jupyter Notebook для решения любых задач</title>
<meta name="description" content="Nbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они">
<link rel="canonical" href="https://ru.hexlet.io/blog/posts/nbdev-ispolzuyte-jupyter-notebook-dlya-resheniya-lyubyh-zadach">
<meta property="og:title" content="Nbdev: используйте Jupyter Notebook для решения любых задач">
<meta property="og:description" content="Nbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они себя ведут.">
<meta property="og:image" content="https://ru.hexlet.io/vite/assets/blog_post-7eTyeLLt.webp">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="dLHXEbIqpdkwBtv7FYVXIlolKpC0tmaCqeMlDTeL1YCbYBwmQFQIuYZF_2MZiqdVmiwHOryBmCAUA79ZZYwy7g" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="/vite/assets/blog_post-7eTyeLLt.webp"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1OCwicHVyIjoiYmxvYl9pZCJ9fQ==--023ea18f500b1c4c91617fa96bbc52df8395da39/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Software%20engineer-bro.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc0OSwicHVyIjoiYmxvYl9pZCJ9fQ==--846349326718432328cf5c0677091aca67f80af3/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-amico%20(1).png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzYyOSwicHVyIjoiYmxvYl9pZCJ9fQ==--f7aaf7f059fd35008c7c2fb722680196834212ab/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Usability%20testing-bro.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0NiwicHVyIjoiYmxvYl9pZCJ9fQ==--5c088db10d02b94be027408f50ecf11c23d9d4cb/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Version%20control-bro.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/blog/posts/show","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T20:52:45.901Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"ajbZaSHwcCrGzmux2CtT3x-Y9Bz6BBr114yobBnKQ2qF5xJe047dSnCNTynUJKOo35HZtvIz5FdqbDI4S82kBA","post":{"model_name":"BlogPost","category":{"id":4,"name":"Код","slug":"code","state":"published","created_at":"2016-08-23T13:33:44.258Z"},"creator":{"public_name":"Дмитрий Дементий","id":174372,"is_tutor":false},"tags":[{"id":542,"slug":"python","name":"Python"},{"id":1119,"slug":"bez-steka","name":"Без стека"}],"id":584,"title":"Nbdev: используйте Jupyter Notebook для решения любых задач","slug":"nbdev-ispolzuyte-jupyter-notebook-dlya-resheniya-lyubyh-zadach","state":"published","summary":"Nbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они себя ведут. Мы исследуем поведение алгоритма, который разрабатываем, чтобы понять, как он работает с разными видами данных. Мы отлаживаем код, пробуя разные комбинации входящих данных, и так далее.","votes_count":3,"created_at":"2019-12-11T16:51:51.149Z","published_at":"2019-12-12T11:53:49.790Z","body":"Это адаптированный перевод статьи Джереми Ховарда (Jeremy Howard) nbdev: use Jupyter Notebooks for everything. Повествование ведётся от лица автора. \n\n> **Крис Латтнер, создатель Swift, LLVM и Swift Playgrounds: «Я действительно считаю, что nbdev — огромный шаг вперёд для программного окружения».**\n\nЯ и мой коллега по fast.ai Сильвен Гаггер (Sylvain Gugger) последние несколько лет работали над любимым проектом. Это программное окружение Python, которое называется nbdev. Это окружение позволяет создавать полноценные пакеты Python, включая тесты и систему документации, в [Jupyter Notebook](https://jupyter.org/). Мы уже написали библиотеку fastai с помощью nbdev. Также сделали с помощью этого инструмента ещё несколько небольших проектов. \n\nNbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они себя ведут. Мы исследуем поведение алгоритма, который разрабатываем, чтобы понять, как он работает с разными видами данных. Мы отлаживаем код, пробуя разные комбинации входящих данных, и так далее.\n\n::programs\n\n## Содержание\n\n## Nbdev: исследовательское программирование\n\nМы верим, что каждое исследование имеет ценность само по себе, и что результаты каждого исследования надо сохранять для других программистов, включая самого исследователя через полгода. Думайте об этом как о дневнике исследователя. Вы можете использовать его, чтобы показать, что вы попробовали, что сработало, а что нет, и что вы сделали, чтобы в конце концов понять систему, с которой работаете. В процессе исследования вы понимаете, что какие-то части понимания критически важны для работы системы. Поэтому исследование должно включать тесты и утверждения (assertions) для закрепления такого поведения. \n\nТакой способ исследования просто организовать, когда вы работаете в REPL или в среде типа Jupyter Notebook. Но эти системы не так хороши для программирования. Поэтому специалисты чаще используют такие системы для ранних исследований, а потом переключаются на текстовый редактор или IDE. Они переключаются, чтобы получить дополнительные возможности, например, подсветку синтаксиса, интеграцию с юнит-тестами, и, самое важное, возможность создать итоговые файлы с исходным кодом, а не код в блокноте или REPL. \n\nNbdev переносит ключевые преимущества IDE в систему ноутбуков. То есть вы можете работать в ноутбуке без ущерба для удобства и результата. \n\nЧтобы достичь нужной интерактивности, nbdev использует Jupyter Notebook в качестве платформы. Это позволяет по максимуму раскрыть динамические свойства языка Python, но привносит и другие инструменты, жизненно необходимые разработчику ПО:\n\n* Автоматическое создание модулей Python, в которых учтены лучшие практики, например, автоматическое определение `__all__` с экспортом функций, классов и переменных.\n* Навигация и редактирование кода в стандартном редакторе или IDE, и автоматический экспорт изменений в ноутбук.\n* Автоматическое создание индексируемой перелинкованной документации. Любое слово, обёрнутое в бэктики, автоматически линкуется с соответствующим разделом документации. В разделе документации создаётся сайдбар, в котором находятся ссылки на модули. \n* Установщики `pip`, загруженные в `pypi`. \n* Тесты, объявленные прямо в блокноте и запускаемые в фоне.\n* Continuous integration (CI).\n* Разрешение конфликтов при версионировании кода.\n\nВот сниппет из нашего актуального исходного кода nbdev, который, конечно же, написан в nbdev. \n\n\n\n_Изучаем формат файлов в исходном коде nbdev_\n___\n\nКак видно, когда вы пишете программы в таком ключе, каждый человек в команде получает пользу от вашей работы. Поскольку вы разрабатываете в ноутбуке, можно также добавлять графики, текст, ссылки, фото, видео и так далее. Этот контент автоматически попадает в документацию. Ячейки, в которых определён код, скрываются и заменяются стандартизированной документацией для вашей функции. В документации отображается имя, аргументы, строки документации, ссылка на исходный код на GitHub.\n\nВ [документации nbdev](https://nbdev.fast.ai/) вы найдёте больше информации о функциональности, установке и способах использования этого инструмента. Как вы помните, эта документация генерируется автоматически. Далее в этой статье я описываю историю и контекст «почему»: почему мы создали инструмент, и почему мы сделали его именно таким.\n\n\n## Инструменты для разработки программного обеспечения\n\nБольшинство инструментов для разработчиков создано без учёта особенностей исследовательского программирования. Когда я начинал программировать около 30 лет назад, практически повсеместно использовалась каскадная модель ([на сленге](https://ru.hexlet.io/blog/posts/ponimaem-sleng-programmistov-mini-slovar-dlya-nachinayuschih-razrabotchikov) её иногда называют «модель водопадов»). Мне казалось, что эта модель, предполагающая точную детализацию и максимально близкое следование документации, не соответствовала тому, как я на самом деле работаю. \n\nВ 90-х годах ситуация изменилась. Популярным стал подход agile. Люди стали понимать, что разработка — итеративный процесс. Появились подходы, которые учитывают это. Однако инструменты разработки существенно не поменялись. В нашем арсенале появились инструменты, которые подходят для TDD (разработка через тестирование). Однако такие инструменты внедрялись скорее как расширения для существующих редакторов, чем как полностью переработанные среды разработки. \n\nВ последние годы мы видим рост интереса к исследовательскому тестированию как к инструменту аджайл-подхода. Это нам интересно, но мы считаем, что этот подход развивается недостаточно глубоко. Мы считаем, что исследования должны быть центральной частью разработки практически всегда. \n\nЛегендарный Дональд Кнут опередил своё время. В 1983 году он представил концепцию [грамотного программирования](https://ru.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D0%BC%D0%BE%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5). Кнут описал её как «методологию, которая сочетает использование языка программирования и языка документирования. Она делает программы более простыми и надёжными. Также методология упрощает разработку программ, написанных на высокоуровневых языках. Главная идея — рассматривать программы как произведения литературы, которые создаются для людей, а не для компьютеров». Я долго был очарован этой идеей, но она, к сожалению, не получила широкого распространения. Инструменты, созданные для такого подхода, замедляли разработку. Это подошло далеко не всем специалистам.\n\nСпустя примерно 30 лет после появления концепции грамотного программирования ещё один яркий разработчик — Брет Виктор — сформулировал претензии к существующим инструментам для программистов. Он рассказал, как создавать [системы программирования для понимания программ](http://worrydream.com/#!/LearnableProgramming). В знаменитой речи [«Принципы изобретательства»](https://jamesclear.com/great-speeches/inventing-on-principle-by-bret-victor) Брет Виктор отметил: «В рамках существующей концепции компьютерные программы рассматриваются как список текстовых определений, которые мы передаём в компилятор. Этот подход родом из 50-х годов прошлого века. Тогда в ходу были Fortran и ALGOL — языки, созданные для работы с перфокартами».\n\nБрет Виктор сформулировал и проиллюстрировал примеры и принципы нового подхода к созданию систем для программирования. Пока никто не реализовал все идеи Виктора. Но попытки частичной имплементации были. Одна из самых известных — Swift Playgrounds Криса Латтнера.\n\n\n\n_Демонстрация Playgrounds в Xcode_\n___\n\nЭто большой шаг вперёд, но здесь всё ещё есть явные ограничения среды, которая создавалась без учёта необходимости исследований. Например, сам процесс исследования здесь вообще не отражается. Тесты тоже не интегрированы. То есть полного внедрения принципов грамотного программирования нет. \n\n\n## Интерактивные среды программирования\n\nЕщё одно направление развития систем программирования — интерактивные среды разработки. Они появились несколько десятилетий назад вместе с LISP и REPL, которые позволили разработчикам интерактивно редактировать код в работающем приложении. Smalltalk пошёл ещё дальше, создав интерактивное визуальное рабочее пространство. Во всех этих случаях языки программирования подходили для интерактивной работы. Например, LISP с его макросистемой и кодом как данными вписался в эту модель. \n\n\n\n_Интерактивное программирование в Smalltalk_\n___\n\nХотя этот подход нельзя назвать общепринятым, он стал популярным в научной сфере, статистике и других областях Data Driven Development ( программирования, управляемого данными). Фронтенд-программирование на JavaScript тоже позаимствовало важные концепции из этого подхода, например, горячую перезагрузку или интерактивное редактирование кода в браузере.\n\n[MATLAB](https://en.wikipedia.org/wiki/MATLAB) тоже создавался как полностью интерактивный инструмент в 70-е годы. Он и сейчас используется в инженерии, биологии и других отраслях. Похожие принципы используются в SPLUS и R, которые широко используются в области статистики и визуализации данных.\n\nЯ был приятно удивлён, когда впервые использовал систему [Mathematica](https://www.wolfram.com/mathematica/) около 25 лет назад. Эта система показалась мне наиболее приближенной к грамотному программированию. Её применение не влияло на продуктивность. В Mathematica используется интерфейс ноутбуков, который ведёт себя практически как REPL. Также система позволяет использовать графики, изображения, форматированный текст и так далее. Эта система позволила мне делать то, что раньше было недоступным. Например, она позволяет тестировать алгоритмы и получать визуальную обратную связь.\n\nНо Mathematica не позволила мне сделать что-то полезное, так как с её помощью невозможно поделиться кодом или приложениями с коллегами, пока вы не оплатите дорогую лицензию. Также Mathematica не позволяет делать приложения, которые можно использовать в браузере. Кроме того, мне показалось, что код Mathematica более медленный и требовательный к памяти, чем код, написанный на других языках.\n\nМожете представить мою радость, когда я узнал о выходе Jupyter Notebook. В нём используется такой же простой интерфейс ноутбуков, как в Mathematica. Но это продукт с открытым исходным кодом, что позволило мне писать на доступных языках с широкой поддержкой. Я использовал Jupyter Notebook не только для изучения алгоритмов, API и проверки новых идей. Этот инструмент стал средством обучения в fast.ai. Многие студенты убедились, что возможность экспериментировать с входящими данными и сразу же видеть результат позволяет лучше усваивать учебный материал. \n\nМы также написали книгу, используя только Jupyter Notebook. Этот инструмент позволяет без затруднений комбинировать текст, примеры кода, иерархически структурированные заголовки. И он гарантирует, что на выходе наш контент выглядит так, как мы ожидаем. \n\nОдним словом, нам и нашим студентам понравился Jupyter Notebook. Оставалось использовать его для разработки нашего программного обеспечения.\n\n\n## Чего не хватает в Jupyter Notebook\n\nJupyter Notebook хорош для исследовательской части исследовательского программирования, но не так хорош для собственно программирования. Например, в нём невозможно:\n\n* Создавать модульный код, который можно переиспользовать и запускать за пределами Jupyter Notebook. \n* Создавать гипертекстовую индексируемую документацию.\n* Тестировать код, включая автоматическое тестирование при непрерывной интеграции.\n* Удобно перемещаться по коду. \n* Контролировать версии.\n\nИз-за таких проблем люди вынуждены переключаться между инструментами, например, использовать IDE или редакторы, REPL/shell, блокноты и так далее. \n\nМы решили, что лучший способ справиться с проблемой — использовать хорошие существующие инструменты, где это возможно, и создавать собственные инструменты, если это необходимо. Например, для управления пулреквестами и отслеживания диффов есть отличный инструмент — [ReviewNB](https://www.reviewnb.com/). Когда вы смотрите на визуальное представление диффов в ReviewNB, понимаете, насколько ограничены стандартные текстовые представления диффов. Например, как можно понять по тексту, что в коммите есть некачественное изображение? Или что в графиках не хватает меток? А благодаря визуальному представлению диффа вы знаете, что происходит.\n\n\n\n\n_Визуальное представление диффа в ReviewNB_\n___\n\nNbdev предупреждает большинство конфликтов при слиянии, так как в инструменте используются хуки, которые автоматически удаляют метаданные, вызывающие большую часть таких конфликтов. Если у вас появляется конфликт при синхронизации репозитория, достаточно запустить `nbdev_fix_merge`. В этом случае nbdev использует ваши исходящие данные, если есть конфликты исходящих данных. Если есть конфликты входящих данных, nbdev включает все данные в итоговый документ и указывает на конфликт. Благодаря этому вы можете быстро найти и исправить проблему. \n\n\n\n\n_Конфликт при слиянии отображён в nbdev_\n___\n\nЧтобы получить модульный переиспользуемый код в nbdev, достаточно создать модули Python. Nbdev ищет в коде специальные комментарии, например, `#export`. Такие комментарии сигнализируют, что ячейку нужно экспортировать в модуль Python. Каждый блокнот связывается с конкретным модулем с помощью специального комментария. Сайт с документацией автоматически создаётся с помощью [генератора статических сайтов Jekyll](https://guides.hexlet.io/jekyll/). Такие сайты можно размещать на GitHub Pages. Мы написали свою систему документации, так как существующие решения, например, Sphinx, не давали нам все необходимые возможности. \n\nВ большинстве редакторов и IDE есть хорошие инструменты для навигации по коду. А GitHub [поддерживает навигацию по коду в веб-интерфейсе](https://help.github.com/en/github/managing-files-in-a-repository/navigating-code-on-github). Поэтому мы обеспечили возможность навигации по коду, который экспортируется из nbdev. Более того, если вы редактируете код в IDE или в веб-интерфейсе, изменения автоматически синхронизируются с nbdev. \n\nДля тестирования мы написали собственную простую библиотеку и утилиту командной строки. Тесты можно писать непосредственно в ноутбуках, это часть исследования и документирования. Естественная statefulness ноутбуков (наличие у них состояния) позволяет использовать как юнит-тесты, так и интеграционные тесты. Вместо изучения нового синтаксиса для создания тестов вы можете использовать коллекции и циклы Python. Созданные тесты можно запускать в среде Continuous Integration (CI), которую вы используете. При этом вы будете получать информацию об источниках ошибок. Nbdev интегрирован с GitHub Actions. Пулреквесты для интеграции с другими платформами приветствуются. \n\n\n## Динамический Python\n\nОдна из проблем с поддержкой Python в IDE или редакторе связана с динамическими свойствами Python. Например, вы в любой момент можете добавить метод в класс, с помощью метаклассов [изменить способы создания классов](https://ru.hexlet.io/blog/posts/prodvinutyy-python-chast-3-klassy-i-metaklassy), а с помощью декораторов [изменять поведение функций и методов](https://ru.hexlet.io/blog/posts/izuchaem-prodvinutye-vozmozhnosti-python-chast-2-zamykaniya-dekoratory-modul-functools). Microsoft создал [Language Server Protocol](https://microsoft.github.io/language-server-protocol/), который может использоваться в средах разработки для получения информации о текущем файле. Использование этого протокола необходимо для важных функций, например, автодополнений, навигации по коду и так далее. Однако при работе с динамическими языками, такими, как Python, подобная информация всегда будет носить характер догадок, так как для получения актуальных данных надо запускать код. Это не всегда возможно по целому ряду причин. \n\nС другой стороны, в ноутбуках есть интерпретатор Python, который вы полностью контролируете. Таким образом Jupyter поддерживает автодополнения, списки параметров, контекстную документацию, которая основана на актуальном состоянии кода. \n\n::posts\n\n## Что дальше\n\nОдновременно с разработкой nbdev мы писали вторую версию fastai полностью в nbdev. В fastai есть структурированное API для создания моделей глубинного обучения (deep learning). Релиз планируется в первой половине 2020 года. Этот продукт уже наполнен функциональностью, и первые пользователи уже создают классные проекты с помощью тестовой версии. \n\nМы заметили рост продуктивности в два или три раза при использовании nbdev по сравнению с другими инструментами. Я программирую около 30 лет практически каждый день, и такой рост продуктивности меня приятно удивил. \n\nСпасибо за интерес к [nbdev](https://github.com/fastai/nbdev/).\n\n_Адаптированный перевод статьи [nbdev: use Jupyter Notebooks for everything](https://www.fast.ai/2019/12/02/nbdev/) by Jeremy Howard._","reading_time":8,"url":"https://ru.hexlet.io/blog/posts/nbdev-ispolzuyte-jupyter-notebook-dlya-resheniya-lyubyh-zadach","cover_thumb_variant":null,"cover_list_variant":"/vite/assets/blog_post-7eTyeLLt.webp","cover_main_variant":"/vite/assets/blog_post-7eTyeLLt.webp","related_stacks_count":5},"relatedPosts":[{"model_name":"BlogPost","id":416,"title":"Язык программирования JavaScript: где его используют и почему он популярен","slug":"stoit-li-uchit-javascript-perspektivy-situatsiya-na-rynke-truda-mneniya-ekspertov","summary":"Рассказываем, что это за язык программирования — JavaScript, где его используют, насколько он популярен и с чего начать изучение JavaScript.","created_at":"2019-07-26T08:17:06.287Z","published_at":"2023-10-04T08:41:58.734Z","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTcwMywicHVyIjoiYmxvYl9pZCJ9fQ==--aa69869c906a992728ba7c26886fa1b715024424/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/%D1%8F%D0%B7%D1%8B%D0%BA%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8Fjavascript-01.png"},{"model_name":"BlogPost","id":480,"title":"Все про Node.js: зачем писать бэкенд на JavaScript и сколько за это платят","slug":"zachem-izuchat-node-js-ili-o-perspektivah-bekenda-na-javascript","summary":"Рассказываем, как устроен бэкенд на JavaScript, стоит ли изучать Node.js, когда есть PHP и Python, какое будущее у этой технологии и сколько платят программистам на Node.js.","created_at":"2019-09-27T08:33:36.150Z","published_at":"2023-07-28T13:31:55.753Z","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTY4MywicHVyIjoiYmxvYl9pZCJ9fQ==--8e5dd13a34bb6d61f1c0db87a25e6e64d0e237fb/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/node%20_new-01.png"},{"model_name":"BlogPost","id":558,"title":"Язык программирования Java: что нужно знать новичку","slug":"yazyk-programmirovaniya-java-osobennosti-populyarnost-situatsiya-na-rynke-truda","summary":"Рассказываем, что это за язык — Java, где его применяют, сколько зарабатывают Java-разработчики и как новичку научиться писать на нем код.","created_at":"2019-11-15T15:35:07.300Z","published_at":"2023-07-04T12:16:16.925Z","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTY2OSwicHVyIjoiYmxvYl9pZCJ9fQ==--052723c2dabf5e97dd0b8eeaa33f767e5debc598/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/%D1%8F%D0%B7%D1%8B%D0%BA%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20java-01.png"}],"category":{"id":4,"name":"Код","slug":"code","state":"published","created_at":"2016-08-23T13:33:44.258Z"},"mainStackCategory":{"id":2,"name":"Курсы по веб-разработке","slug":"web_development","short_name":"Веб-разработка","order":190,"state":"published","category_slug":"courses_web_development"},"categories":[{"id":6,"name":"Мотивация","slug":"motivation","state":"published","created_at":"2016-10-06T18:31:38.903Z"},{"id":3,"name":"Истории успеха","slug":"success","state":"published","created_at":"2016-07-30T12:57:18.308Z"},{"id":14,"name":"Дневник студента","slug":"student-diary","state":"published","created_at":"2019-02-25T13:27:09.471Z"},{"id":4,"name":"Код","slug":"code","state":"published","created_at":"2016-08-23T13:33:44.258Z"},{"id":12,"name":"Карьера","slug":"career","state":"published","created_at":"2017-07-21T15:42:21.481Z"}],"relatedLandings":[{"stack":{"id":7,"slug":"python","title":"Python-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":10,"duration_in_months":10},"id":7,"slug":"python","title":"Python-разработчик ","subtitle":"Изучите Python, Django, REST и Fast API для создания веб-приложений","subtitle_for_lists":"Изучите Python, Django, REST и Fast API для создания веб-приложений","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"python","price_text":"от 6 792 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"},{"stack":{"id":37,"slug":"python-sicp","title":"СИКП на Python","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4150,"duration_in_months":1},"id":62,"slug":"python-sicp","title":"СИКП на Python","subtitle":"Навык понимать код на фундаментальном уровне, уверенно проходить собеседования и решать сложные задачи","subtitle_for_lists":"Изучите Python на глубоком уровне для решения сложных задач","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"python-sicp","price_text":"от 3 900 ₽","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1OCwicHVyIjoiYmxvYl9pZCJ9fQ==--023ea18f500b1c4c91617fa96bbc52df8395da39/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Software%20engineer-bro.png"},{"stack":{"id":67,"slug":"python-oop","title":"ООП на Python","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4350,"duration_in_months":2},"id":120,"slug":"python-oop","title":"ООП на Python","subtitle":"Навык понимания архитектуры и чистого кода, позволяющий проходить собеседования, решать задачи и увеличивать зарплату","subtitle_for_lists":"Изучите архитектуру и чистый код на Python","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"python-oop","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc0OSwicHVyIjoiYmxvYl9pZCJ9fQ==--846349326718432328cf5c0677091aca67f80af3/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-amico%20(1).png"},{"stack":{"id":40,"slug":"python-pytest-testing","title":"Pytest","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":500,"duration_in_months":1},"id":68,"slug":"python-pytest-testing","title":"Pytest","subtitle":"Навык писать автотесты на Python, тестировать приложения и повышать их качество, улучшая надёжность кода","subtitle_for_lists":"Получите навык тестирования на Pytest","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"python-pytest-testing","price_text":"от 3 900 ₽","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzYyOSwicHVyIjoiYmxvYl9pZCJ9fQ==--f7aaf7f059fd35008c7c2fb722680196834212ab/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Usability%20testing-bro.png"},{"stack":{"id":24,"slug":"python-django-development","title":"Django","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":300,"duration_in_months":2},"id":36,"slug":"python-django-developer","title":"Django","subtitle":"Навык создания веб-приложений на Django, необходимый для получения оффера на позицию Python-разработчика","subtitle_for_lists":"Изучите фреймворк Django для создания веб-приложений ","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"python-django-development","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0NiwicHVyIjoiYmxvYl9pZCJ9fQ==--5c088db10d02b94be027408f50ecf11c23d9d4cb/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Version%20control-bro.png"}]},"url":"/blog/posts/nbdev-ispolzuyte-jupyter-notebook-dlya-resheniya-lyubyh-zadach","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","author":"Дмитрий Дементий","name":"Nbdev: используйте Jupyter Notebook для решения любых задач","datePublished":"2019-12-12T11:53:49.790Z","headline":"Nbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они себя ведут. Мы исследуем поведение алгоритма, который разрабатываем, чтобы понять, как он работает с разными видами данных. Мы отлаживаем код, пробуя разные комбинации входящих данных, и так далее.","image":"/vite/assets/blog_post-7eTyeLLt.webp","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3}]}</script><div style="--container-size:var(--container-size-lg);margin-top:var(--mantine-spacing-xl);height:100%" class="m_7485cace mantine-Container-root" data-size="lg" data-strategy="block"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"position":1,"@type":"ListItem","item":{"@id":"/blog","name":"Блог Хекслета"}},{"position":2,"@type":"ListItem","item":{"@id":"/blog/categories/code","name":"Код"}},{"position":3,"@type":"ListItem","item":{"@id":"/blog/posts/nbdev-ispolzuyte-jupyter-notebook-dlya-resheniya-lyubyh-zadach","name":"Nbdev: используйте Jupyter Notebook для решения любых задач"}}]}</script><div style="margin-bottom:var(--mantine-spacing-xs)" class="m_8b3717df mantine-Breadcrumbs-root"><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/"><div style="color:inherit" class="m_4451eb3a mantine-Center-root"><svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-home-link "><path d="M20.085 11.085l-8.085 -8.085l-9 9h2v7a2 2 0 0 0 2 2h4.5"></path><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 1.807 1.143"></path><path d="M20 21a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M20 16a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M15 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M21 16l-5 3l5 2"></path></svg></div></a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/blog">Блог Хекслета</a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><a style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:inherit" class="mantine-focus-auto m_849cf0da m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-size="sm" data-underline="hover" href="/blog/categories/code">Код</a><div class="m_3b8f2208 mantine-Breadcrumbs-separator">/</div><p style="--text-fz:var(--mantine-font-size-sm);--text-lh:var(--mantine-line-height-sm);white-space:normal;color:var(--mantine-color-dimmed)" class="mantine-focus-auto m_f678d540 mantine-Breadcrumbs-breadcrumb m_b6d8b162 mantine-Text-root" data-size="sm">Nbdev: используйте Jupyter Notebook для решения любых задач</p></div><style data-mantine-styles="inline">.__m__-_R_eub_{margin-bottom:var(--mantine-spacing-xs);}@media(min-width: 36em){.__m__-_R_eub_{margin-bottom:var(--mantine-spacing-xs);}}</style><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root __m__-_R_eub_"><style data-mantine-styles="inline">.__m__-_R_deub_{width:100%;}@media(min-width: 36em){.__m__-_R_deub_{width:70%;}}@media(min-width: 75em){.__m__-_R_deub_{width:75%;}}</style><div class="__m__-_R_deub_"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size)" class="m_8a5d1357 mantine-Title-root" data-order="1">Nbdev: используйте Jupyter Notebook для решения любых задач</h1></div></div></div><div style="position:absolute;top:calc(18.75rem * var(--mantine-scale))" class=""></div><style data-mantine-styles="inline">.__m__-_R_2iub_{--grid-gutter:var(--mantine-spacing-xl);}</style><div class="m_410352e9 mantine-Grid-root __m__-_R_2iub_"><div class="m_dee7bd2f mantine-Grid-inner"><style data-mantine-styles="inline">.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:100%;--col-max-width:100%;}@media(min-width: 48em){.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}@media(min-width: 62em){.__m__-_R_dmiub_{--col-flex-grow:auto;--col-flex-basis:66.66666666666667%;--col-max-width:66.66666666666667%;}}</style><div class="m_96bdd299 mantine-Grid-col __m__-_R_dmiub_"><div style="--stack-gap:var(--mantine-spacing-md);--stack-align:stretch;--stack-justify:flex-start;margin-bottom:var(--mantine-spacing-xl)" class="m_6d731127 mantine-Stack-root"><div class=""><div style="--group-gap:var(--mantine-spacing-xs);--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-xl)" class="m_4081bf90 mantine-Group-root"><button style="--badge-height:var(--badge-height-sm);--badge-padding-x:var(--badge-padding-x-sm);--badge-fz:var(--badge-fz-sm);--badge-bg:var(--mantine-color-default);--badge-color:var(--mantine-color-default-color);--badge-bd:calc(0.0625rem * var(--mantine-scale)) solid var(--mantine-color-default-border);cursor:pointer;color:inherit" class="m_347db0ec mantine-Badge-root" data-variant="default" data-size="sm" type="button" aria-label="Python"><span class="m_5add502a mantine-Badge-label">Python</span></button><button style="--badge-height:var(--badge-height-sm);--badge-padding-x:var(--badge-padding-x-sm);--badge-fz:var(--badge-fz-sm);--badge-bg:var(--mantine-color-default);--badge-color:var(--mantine-color-default-color);--badge-bd:calc(0.0625rem * var(--mantine-scale)) solid var(--mantine-color-default-border);cursor:pointer;color:inherit" class="m_347db0ec mantine-Badge-root" data-variant="default" data-size="sm" type="button" aria-label="Без стека"><span class="m_5add502a mantine-Badge-label">Без стека</span></button></div><div style="--group-gap:calc(0.625rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-sm);color:var(--mantine-color-gray-text)" class="m_4081bf90 mantine-Group-root"><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap;margin-inline-end:var(--mantine-spacing-lg)" class="m_4081bf90 mantine-Group-root">12 декабря 2019 г.</div><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-clock "><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"></path><path d="M12 7v5l3 3"></path></svg></div>8 минут</div><div style="--group-gap:calc(0.1875rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-thumb-up "><path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path></svg></div>3</div></div><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img style="--image-radius:var(--mantine-radius-md);--image-object-fit:cover;width:100%;height:100%" class="m_9e117634 mantine-Image-root" src="/vite/assets/blog_post-7eTyeLLt.webp" alt="Nbdev: используйте Jupyter Notebook для решения любых задач"/></div></div><div role="link" tabindex="0" style="cursor:pointer"><button style="display:block;width:100%" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Присоединяйтесь к нашему Telegram-сообществу"><div style="background-color:light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6))" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:flex-start;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><div style="--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:auto;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-brand-telegram "><path d="M15 10l-4 4l6 6l4 -16l-18 7l4 2l2 6l3 -4"></path></svg></div>Присоединяйтесь к нашему Telegram-сообществу</div></div></button></div><div style="margin-bottom:var(--mantine-spacing-xl)" class="m_d08caa0 mantine-Typography-root"><p>Это адаптированный перевод статьи Джереми Ховарда (Jeremy Howard) nbdev: use Jupyter Notebooks for everything. Повествование ведётся от лица автора.</p>
<blockquote>
<p><strong>Крис Латтнер, создатель Swift, LLVM и Swift Playgrounds: «Я действительно считаю, что nbdev — огромный шаг вперёд для программного окружения».</strong></p>
</blockquote>
<p>Я и мой коллега по fast.ai Сильвен Гаггер (Sylvain Gugger) последние несколько лет работали над любимым проектом. Это программное окружение Python, которое называется nbdev. Это окружение позволяет создавать полноценные пакеты Python, включая тесты и систему документации, в <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://jupyter.org/" rel="noopener noreferrer" target="_blank">Jupyter Notebook</a>. Мы уже написали библиотеку fastai с помощью nbdev. Также сделали с помощью этого инструмента ещё несколько небольших проектов.</p>
<p>Nbdev — система для исследовательского программирования. Этот подход базируется на утверждении, что большинство программистов проводят большую часть рабочего времени, экспериментируя и тестируя гипотезы. Мы экспериментируем с новыми API, которые используем впервые, чтобы для начала понять, как они себя ведут. Мы исследуем поведение алгоритма, который разрабатываем, чтобы понять, как он работает с разными видами данных. Мы отлаживаем код, пробуя разные комбинации входящих данных, и так далее.</p>
<style data-mantine-styles="inline">.__m__-_R_9derddmiub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_9derddmiub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_9derddmiub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Python-разработчик </p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Python, Django, REST и Fast API для создания веб-приложений</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png" alt="Python-разработчик " loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python-sicp?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">1 месяц</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">СИКП на Python</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Python на глубоком уровне для решения сложных задач</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1OCwicHVyIjoiYmxvYl9pZCJ9fQ==--023ea18f500b1c4c91617fa96bbc52df8395da39/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Software%20engineer-bro.png" alt="СИКП на Python" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python-oop?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">ООП на Python</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите архитектуру и чистый код на Python</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc0OSwicHVyIjoiYmxvYl9pZCJ9fQ==--846349326718432328cf5c0677091aca67f80af3/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-amico%20(1).png" alt="ООП на Python" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python-pytest-testing?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">1 месяц</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Pytest</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Получите навык тестирования на Pytest</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzYyOSwicHVyIjoiYmxvYl9pZCJ9fQ==--f7aaf7f059fd35008c7c2fb722680196834212ab/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Usability%20testing-bro.png" alt="Pytest" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python-django-developer?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Django</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите фреймворк Django для создания веб-приложений </p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0NiwicHVyIjoiYmxvYl9pZCJ9fQ==--5c088db10d02b94be027408f50ecf11c23d9d4cb/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Version%20control-bro.png" alt="Django" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=blog_post&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div>
<h2 id="heading-2-1">Содержание</h2>
<ul>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-2">Nbdev: исследовательское программирование</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-3">Инструменты для разработки программного обеспечения</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-4">Интерактивные среды программирования</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-5">Чего не хватает в Jupyter Notebook</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-6">Динамический Python</a></li>
<li><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="#heading-2-7">Что дальше</a></li>
</ul>
<h2 id="heading-2-2">Nbdev: исследовательское программирование</h2>
<p>Мы верим, что каждое исследование имеет ценность само по себе, и что результаты каждого исследования надо сохранять для других программистов, включая самого исследователя через полгода. Думайте об этом как о дневнике исследователя. Вы можете использовать его, чтобы показать, что вы попробовали, что сработало, а что нет, и что вы сделали, чтобы в конце концов понять систему, с которой работаете. В процессе исследования вы понимаете, что какие-то части понимания критически важны для работы системы. Поэтому исследование должно включать тесты и утверждения (assertions) для закрепления такого поведения.</p>
<p>Такой способ исследования просто организовать, когда вы работаете в REPL или в среде типа Jupyter Notebook. Но эти системы не так хороши для программирования. Поэтому специалисты чаще используют такие системы для ранних исследований, а потом переключаются на текстовый редактор или IDE. Они переключаются, чтобы получить дополнительные возможности, например, подсветку синтаксиса, интеграцию с юнит-тестами, и, самое важное, возможность создать итоговые файлы с исходным кодом, а не код в блокноте или REPL.</p>
<p>Nbdev переносит ключевые преимущества IDE в систему ноутбуков. То есть вы можете работать в ноутбуке без ущерба для удобства и результата.</p>
<p>Чтобы достичь нужной интерактивности, nbdev использует Jupyter Notebook в качестве платформы. Это позволяет по максимуму раскрыть динамические свойства языка Python, но привносит и другие инструменты, жизненно необходимые разработчику ПО:</p>
<ul>
<li>Автоматическое создание модулей Python, в которых учтены лучшие практики, например, автоматическое определение <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">__all__</code> с экспортом функций, классов и переменных.</li>
<li>Навигация и редактирование кода в стандартном редакторе или IDE, и автоматический экспорт изменений в ноутбук.</li>
<li>Автоматическое создание индексируемой перелинкованной документации. Любое слово, обёрнутое в бэктики, автоматически линкуется с соответствующим разделом документации. В разделе документации создаётся сайдбар, в котором находятся ссылки на модули.</li>
<li>Установщики <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">pip</code>, загруженные в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">pypi</code>.</li>
<li>Тесты, объявленные прямо в блокноте и запускаемые в фоне.</li>
<li>Continuous integration (CI).</li>
<li>Разрешение конфликтов при версионировании кода.</li>
</ul>
<p>Вот сниппет из нашего актуального исходного кода nbdev, который, конечно же, написан в nbdev.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/STyPSfg.jpg" alt="код в nbdev" loading="lazy"/></p>
<p><em>Изучаем формат файлов в исходном коде nbdev</em></p>
<hr/>
<p>Как видно, когда вы пишете программы в таком ключе, каждый человек в команде получает пользу от вашей работы. Поскольку вы разрабатываете в ноутбуке, можно также добавлять графики, текст, ссылки, фото, видео и так далее. Этот контент автоматически попадает в документацию. Ячейки, в которых определён код, скрываются и заменяются стандартизированной документацией для вашей функции. В документации отображается имя, аргументы, строки документации, ссылка на исходный код на GitHub.</p>
<p>В <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://nbdev.fast.ai/" rel="noopener noreferrer" target="_blank">документации nbdev</a> вы найдёте больше информации о функциональности, установке и способах использования этого инструмента. Как вы помните, эта документация генерируется автоматически. Далее в этой статье я описываю историю и контекст «почему»: почему мы создали инструмент, и почему мы сделали его именно таким.</p>
<h2 id="heading-2-3">Инструменты для разработки программного обеспечения</h2>
<p>Большинство инструментов для разработчиков создано без учёта особенностей исследовательского программирования. Когда я начинал программировать около 30 лет назад, практически повсеместно использовалась каскадная модель (<a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/blog/posts/ponimaem-sleng-programmistov-mini-slovar-dlya-nachinayuschih-razrabotchikov" rel="noopener noreferrer" target="_blank">на сленге</a> её иногда называют «модель водопадов»). Мне казалось, что эта модель, предполагающая точную детализацию и максимально близкое следование документации, не соответствовала тому, как я на самом деле работаю.</p>
<p>В 90-х годах ситуация изменилась. Популярным стал подход agile. Люди стали понимать, что разработка — итеративный процесс. Появились подходы, которые учитывают это. Однако инструменты разработки существенно не поменялись. В нашем арсенале появились инструменты, которые подходят для TDD (разработка через тестирование). Однако такие инструменты внедрялись скорее как расширения для существующих редакторов, чем как полностью переработанные среды разработки.</p>
<p>В последние годы мы видим рост интереса к исследовательскому тестированию как к инструменту аджайл-подхода. Это нам интересно, но мы считаем, что этот подход развивается недостаточно глубоко. Мы считаем, что исследования должны быть центральной частью разработки практически всегда.</p>
<p>Легендарный Дональд Кнут опередил своё время. В 1983 году он представил концепцию <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D0%BC%D0%BE%D1%82%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5" rel="noopener noreferrer" target="_blank">грамотного программирования</a>. Кнут описал её как «методологию, которая сочетает использование языка программирования и языка документирования. Она делает программы более простыми и надёжными. Также методология упрощает разработку программ, написанных на высокоуровневых языках. Главная идея — рассматривать программы как произведения литературы, которые создаются для людей, а не для компьютеров». Я долго был очарован этой идеей, но она, к сожалению, не получила широкого распространения. Инструменты, созданные для такого подхода, замедляли разработку. Это подошло далеко не всем специалистам.</p>
<p>Спустя примерно 30 лет после появления концепции грамотного программирования ещё один яркий разработчик — Брет Виктор — сформулировал претензии к существующим инструментам для программистов. Он рассказал, как создавать <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="http://worrydream.com/#!/LearnableProgramming" rel="noopener noreferrer" target="_blank">системы программирования для понимания программ</a>. В знаменитой речи <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://jamesclear.com/great-speeches/inventing-on-principle-by-bret-victor" rel="noopener noreferrer" target="_blank">«Принципы изобретательства»</a> Брет Виктор отметил: «В рамках существующей концепции компьютерные программы рассматриваются как список текстовых определений, которые мы передаём в компилятор. Этот подход родом из 50-х годов прошлого века. Тогда в ходу были Fortran и ALGOL — языки, созданные для работы с перфокартами».</p>
<p>Брет Виктор сформулировал и проиллюстрировал примеры и принципы нового подхода к созданию систем для программирования. Пока никто не реализовал все идеи Виктора. Но попытки частичной имплементации были. Одна из самых известных — Swift Playgrounds Криса Латтнера.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/6c7Ca0j.jpg" alt="Playgrounds в Xcode" loading="lazy"/></p>
<p><em>Демонстрация Playgrounds в Xcode</em></p>
<hr/>
<p>Это большой шаг вперёд, но здесь всё ещё есть явные ограничения среды, которая создавалась без учёта необходимости исследований. Например, сам процесс исследования здесь вообще не отражается. Тесты тоже не интегрированы. То есть полного внедрения принципов грамотного программирования нет.</p>
<h2 id="heading-2-4">Интерактивные среды программирования</h2>
<p>Ещё одно направление развития систем программирования — интерактивные среды разработки. Они появились несколько десятилетий назад вместе с LISP и REPL, которые позволили разработчикам интерактивно редактировать код в работающем приложении. Smalltalk пошёл ещё дальше, создав интерактивное визуальное рабочее пространство. Во всех этих случаях языки программирования подходили для интерактивной работы. Например, LISP с его макросистемой и кодом как данными вписался в эту модель.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/R8S8rxT.jpg" alt="Smalltalk" loading="lazy"/></p>
<p><em>Интерактивное программирование в Smalltalk</em></p>
<hr/>
<p>Хотя этот подход нельзя назвать общепринятым, он стал популярным в научной сфере, статистике и других областях Data Driven Development ( программирования, управляемого данными). Фронтенд-программирование на JavaScript тоже позаимствовало важные концепции из этого подхода, например, горячую перезагрузку или интерактивное редактирование кода в браузере.</p>
<p><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/MATLAB" rel="noopener noreferrer" target="_blank">MATLAB</a> тоже создавался как полностью интерактивный инструмент в 70-е годы. Он и сейчас используется в инженерии, биологии и других отраслях. Похожие принципы используются в SPLUS и R, которые широко используются в области статистики и визуализации данных.</p>
<p>Я был приятно удивлён, когда впервые использовал систему <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://www.wolfram.com/mathematica/" rel="noopener noreferrer" target="_blank">Mathematica</a> около 25 лет назад. Эта система показалась мне наиболее приближенной к грамотному программированию. Её применение не влияло на продуктивность. В Mathematica используется интерфейс ноутбуков, который ведёт себя практически как REPL. Также система позволяет использовать графики, изображения, форматированный текст и так далее. Эта система позволила мне делать то, что раньше было недоступным. Например, она позволяет тестировать алгоритмы и получать визуальную обратную связь.</p>
<p>Но Mathematica не позволила мне сделать что-то полезное, так как с её помощью невозможно поделиться кодом или приложениями с коллегами, пока вы не оплатите дорогую лицензию. Также Mathematica не позволяет делать приложения, которые можно использовать в браузере. Кроме того, мне показалось, что код Mathematica более медленный и требовательный к памяти, чем код, написанный на других языках.</p>
<p>Можете представить мою радость, когда я узнал о выходе Jupyter Notebook. В нём используется такой же простой интерфейс ноутбуков, как в Mathematica. Но это продукт с открытым исходным кодом, что позволило мне писать на доступных языках с широкой поддержкой. Я использовал Jupyter Notebook не только для изучения алгоритмов, API и проверки новых идей. Этот инструмент стал средством обучения в fast.ai. Многие студенты убедились, что возможность экспериментировать с входящими данными и сразу же видеть результат позволяет лучше усваивать учебный материал.</p>
<p>Мы также написали книгу, используя только Jupyter Notebook. Этот инструмент позволяет без затруднений комбинировать текст, примеры кода, иерархически структурированные заголовки. И он гарантирует, что на выходе наш контент выглядит так, как мы ожидаем.</p>
<p>Одним словом, нам и нашим студентам понравился Jupyter Notebook. Оставалось использовать его для разработки нашего программного обеспечения.</p>
<h2 id="heading-2-5">Чего не хватает в Jupyter Notebook</h2>
<p>Jupyter Notebook хорош для исследовательской части исследовательского программирования, но не так хорош для собственно программирования. Например, в нём невозможно:</p>
<ul>
<li>Создавать модульный код, который можно переиспользовать и запускать за пределами Jupyter Notebook.</li>
<li>Создавать гипертекстовую индексируемую документацию.</li>
<li>Тестировать код, включая автоматическое тестирование при непрерывной интеграции.</li>
<li>Удобно перемещаться по коду.</li>
<li>Контролировать версии.</li>
</ul>
<p>Из-за таких проблем люди вынуждены переключаться между инструментами, например, использовать IDE или редакторы, REPL/shell, блокноты и так далее.</p>
<p>Мы решили, что лучший способ справиться с проблемой — использовать хорошие существующие инструменты, где это возможно, и создавать собственные инструменты, если это необходимо. Например, для управления пулреквестами и отслеживания диффов есть отличный инструмент — <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://www.reviewnb.com/" rel="noopener noreferrer" target="_blank">ReviewNB</a>. Когда вы смотрите на визуальное представление диффов в ReviewNB, понимаете, насколько ограничены стандартные текстовые представления диффов. Например, как можно понять по тексту, что в коммите есть некачественное изображение? Или что в графиках не хватает меток? А благодаря визуальному представлению диффа вы знаете, что происходит.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/JfnVCA0.jpg" alt="дифф в визуальном представлении" loading="lazy"/></p>
<p><em>Визуальное представление диффа в ReviewNB</em></p>
<hr/>
<p>Nbdev предупреждает большинство конфликтов при слиянии, так как в инструменте используются хуки, которые автоматически удаляют метаданные, вызывающие большую часть таких конфликтов. Если у вас появляется конфликт при синхронизации репозитория, достаточно запустить <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">nbdev_fix_merge</code>. В этом случае nbdev использует ваши исходящие данные, если есть конфликты исходящих данных. Если есть конфликты входящих данных, nbdev включает все данные в итоговый документ и указывает на конфликт. Благодаря этому вы можете быстро найти и исправить проблему.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="https://i.imgur.com/KDUcytY.jpg" alt="конфликт при мёрже" loading="lazy"/></p>
<p><em>Конфликт при слиянии отображён в nbdev</em></p>
<hr/>
<p>Чтобы получить модульный переиспользуемый код в nbdev, достаточно создать модули Python. Nbdev ищет в коде специальные комментарии, например, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">#export</code>. Такие комментарии сигнализируют, что ячейку нужно экспортировать в модуль Python. Каждый блокнот связывается с конкретным модулем с помощью специального комментария. Сайт с документацией автоматически создаётся с помощью <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://guides.hexlet.io/jekyll/" rel="noopener noreferrer" target="_blank">генератора статических сайтов Jekyll</a>. Такие сайты можно размещать на GitHub Pages. Мы написали свою систему документации, так как существующие решения, например, Sphinx, не давали нам все необходимые возможности.</p>
<p>В большинстве редакторов и IDE есть хорошие инструменты для навигации по коду. А GitHub <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://help.github.com/en/github/managing-files-in-a-repository/navigating-code-on-github" rel="noopener noreferrer" target="_blank">поддерживает навигацию по коду в веб-интерфейсе</a>. Поэтому мы обеспечили возможность навигации по коду, который экспортируется из nbdev. Более того, если вы редактируете код в IDE или в веб-интерфейсе, изменения автоматически синхронизируются с nbdev.</p>
<p>Для тестирования мы написали собственную простую библиотеку и утилиту командной строки. Тесты можно писать непосредственно в ноутбуках, это часть исследования и документирования. Естественная statefulness ноутбуков (наличие у них состояния) позволяет использовать как юнит-тесты, так и интеграционные тесты. Вместо изучения нового синтаксиса для создания тестов вы можете использовать коллекции и циклы Python. Созданные тесты можно запускать в среде Continuous Integration (CI), которую вы используете. При этом вы будете получать информацию об источниках ошибок. Nbdev интегрирован с GitHub Actions. Пулреквесты для интеграции с другими платформами приветствуются.</p>
<h2 id="heading-2-6">Динамический Python</h2>
<p>Одна из проблем с поддержкой Python в IDE или редакторе связана с динамическими свойствами Python. Например, вы в любой момент можете добавить метод в класс, с помощью метаклассов <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/blog/posts/prodvinutyy-python-chast-3-klassy-i-metaklassy" rel="noopener noreferrer" target="_blank">изменить способы создания классов</a>, а с помощью декораторов <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.hexlet.io/blog/posts/izuchaem-prodvinutye-vozmozhnosti-python-chast-2-zamykaniya-dekoratory-modul-functools" rel="noopener noreferrer" target="_blank">изменять поведение функций и методов</a>. Microsoft создал <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://microsoft.github.io/language-server-protocol/" rel="noopener noreferrer" target="_blank">Language Server Protocol</a>, который может использоваться в средах разработки для получения информации о текущем файле. Использование этого протокола необходимо для важных функций, например, автодополнений, навигации по коду и так далее. Однако при работе с динамическими языками, такими, как Python, подобная информация всегда будет носить характер догадок, так как для получения актуальных данных надо запускать код. Это не всегда возможно по целому ряду причин.</p>
<p>С другой стороны, в ноутбуках есть интерпретатор Python, который вы полностью контролируете. Таким образом Jupyter поддерживает автодополнения, списки параметров, контекстную документацию, которая основана на актуальном состоянии кода.</p>
<style data-mantine-styles="inline">.__m__-_R_3pderddmiub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:80%;}@media(min-width: 36em){.__m__-_R_3pderddmiub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_3pderddmiub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/stoit-li-uchit-javascript-perspektivy-situatsiya-na-rynke-truda-mneniya-ekspertov"><div style="padding-top:0rem;height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="margin-bottom:var(--mantine-spacing-sm)" class="m_599a2148 mantine-Card-section" data-first-section="true"><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img class="m_9e117634 mantine-Image-root" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTcwMywicHVyIjoiYmxvYl9pZCJ9fQ==--aa69869c906a992728ba7c26886fa1b715024424/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/%D1%8F%D0%B7%D1%8B%D0%BA%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8Fjavascript-01.png" loading="lazy" alt="Язык программирования JavaScript: где его используют и почему он популярен"/></div></div><p style="margin-bottom:var(--mantine-spacing-xs);font-size:var(--mantine-font-size-lg);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Язык программирования JavaScript: где его используют и почему он популярен</p><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Рассказываем, что это за язык программирования — JavaScript, где его используют, насколько он поп...</p><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-sm)" class="m_4081bf90 mantine-Group-root">4 октября 2023 г.<p style="font-size:inherit" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/zachem-izuchat-node-js-ili-o-perspektivah-bekenda-na-javascript"><div style="padding-top:0rem;height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="margin-bottom:var(--mantine-spacing-sm)" class="m_599a2148 mantine-Card-section" data-first-section="true"><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img class="m_9e117634 mantine-Image-root" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTY4MywicHVyIjoiYmxvYl9pZCJ9fQ==--8e5dd13a34bb6d61f1c0db87a25e6e64d0e237fb/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/node%20_new-01.png" loading="lazy" alt="Все про Node.js: зачем писать бэкенд на JavaScript и сколько за это платят"/></div></div><p style="margin-bottom:var(--mantine-spacing-xs);font-size:var(--mantine-font-size-lg);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Все про Node.js: зачем писать бэкенд на JavaScript и сколько за это платят</p><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Рассказываем, как устроен бэкенд на JavaScript, стоит ли изучать Node.js, когда есть PHP и Python...</p><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-sm)" class="m_4081bf90 mantine-Group-root">28 июля 2023 г.<p style="font-size:inherit" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/yazyk-programmirovaniya-java-osobennosti-populyarnost-situatsiya-na-rynke-truda"><div style="padding-top:0rem;height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="margin-bottom:var(--mantine-spacing-sm)" class="m_599a2148 mantine-Card-section" data-first-section="true"><div style="--ar-ratio:2" class="m_71ac47fc mantine-AspectRatio-root"><img class="m_9e117634 mantine-Image-root" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTY2OSwicHVyIjoiYmxvYl9pZCJ9fQ==--052723c2dabf5e97dd0b8eeaa33f767e5debc598/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX2FuZF9wYWQiOls3MDgsMzU0XSwic2F2ZXIiOnsicXVhbGl0eSI6ODV9fSwicHVyIjoidmFyaWF0aW9uIn19--324dc52aa55ebe818c2a887ebcb832b9ad1c0381/%D1%8F%D0%B7%D1%8B%D0%BA%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20java-01.png" loading="lazy" alt="Язык программирования Java: что нужно знать новичку"/></div></div><p style="margin-bottom:var(--mantine-spacing-xs);font-size:var(--mantine-font-size-lg);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Язык программирования Java: что нужно знать новичку</p><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Рассказываем, что это за язык — Java, где его применяют, сколько зарабатывают Java-разработчики и...</p><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-sm)" class="m_4081bf90 mantine-Group-root">4 июля 2023 г.<p style="font-size:inherit" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></a></div></div></div></div></div>
<h2 id="heading-2-7">Что дальше</h2>
<p>Одновременно с разработкой nbdev мы писали вторую версию fastai полностью в nbdev. В fastai есть структурированное API для создания моделей глубинного обучения (deep learning). Релиз планируется в первой половине 2020 года. Этот продукт уже наполнен функциональностью, и первые пользователи уже создают классные проекты с помощью тестовой версии.</p>
<p>Мы заметили рост продуктивности в два или три раза при использовании nbdev по сравнению с другими инструментами. Я программирую около 30 лет практически каждый день, и такой рост продуктивности меня приятно удивил.</p>
<p>Спасибо за интерес к <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/fastai/nbdev/" rel="noopener noreferrer" target="_blank">nbdev</a>.</p>
<p><em>Адаптированный перевод статьи <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://www.fast.ai/2019/12/02/nbdev/" rel="noopener noreferrer" target="_blank">nbdev: use Jupyter Notebooks for everything</a> by Jeremy Howard.</em></p></div><div class=""><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap;margin-bottom:var(--mantine-spacing-lg)" class="m_4081bf90 mantine-Group-root"><div class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:var(--mantine-spacing-xs);color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-user "><path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"></path><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"></path></svg></div><p style="margin-inline-end:var(--mantine-spacing-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дмитрий Дементий</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">6 лет назад</p></div><div style="align-items:center" class="m_8bffd616 mantine-Flex-root __m__-_R_5dirddmiub_"><a style="display:inline-flex" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/nbdev-ispolzuyte-jupyter-notebook-dlya-resheniya-lyubyh-zadach/votes"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:var(--mantine-spacing-xs);color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-thumb-up "><path d="M7 11v8a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-7a1 1 0 0 1 1 -1h3a4 4 0 0 0 4 -4v-1a2 2 0 0 1 4 0v5h3a2 2 0 0 1 2 2l-1 5a2 3 0 0 1 -2 2h-7a3 3 0 0 1 -3 -3"></path></svg></div></a><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">3</p></div></div></div><div style="background-color:var(--mantine-color-indigo-light);border:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding:var(--mantine-spacing-xl)" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Читайте также:</p><ul style="margin-inline-start:var(--mantine-spacing-lg)" class="m_abbac491 mantine-List-root"><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/stoit-li-uchit-javascript-perspektivy-situatsiya-na-rynke-truda-mneniya-ekspertov">Язык программирования JavaScript: где его используют и почему он популярен</a></span></div></li><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/zachem-izuchat-node-js-ili-o-perspektivah-bekenda-na-javascript">Все про Node.js: зачем писать бэкенд на JavaScript и сколько за это платят</a></span></div></li><li style="margin-bottom:var(--mantine-spacing-sm)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><a style="color:inherit" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/blog/posts/yazyk-programmirovaniya-java-osobennosti-populyarnost-situatsiya-na-rynke-truda">Язык программирования Java: что нужно знать новичку</a></span></div></li></ul></div><div style="margin-block:var(--mantine-spacing-xl)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div></div><div></div></div><style data-mantine-styles="inline">.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:100%;--col-max-width:100%;}@media(min-width: 48em){.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}@media(min-width: 62em){.__m__-_R_lmiub_{--col-flex-grow:auto;--col-flex-basis:33.333333333333336%;--col-max-width:33.333333333333336%;}}</style><div class="m_96bdd299 mantine-Grid-col __m__-_R_lmiub_ mantine-visible-from-md"><div style="background-color:var(--mantine-color-indigo-light);border:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-bottom:var(--mantine-spacing-xl);padding:var(--mantine-spacing-xl);width:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div style="margin-bottom:var(--mantine-spacing-md)" class="m_4451eb3a mantine-Center-root" data-inline="true"><p style="font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Категории</p></div><ul class="m_abbac491 mantine-List-root"><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Мотивация">Мотивация</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Истории успеха">Истории успеха</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Дневник студента">Дневник студента</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Код">Код</button></span></div></li><li style="margin-bottom:var(--mantine-spacing-xs)" class="m_abb6bec2 mantine-List-item" data-with-icon="true"><div class="m_75cd9f71 mantine-List-itemWrapper"><span class="m_60f83e5b mantine-List-itemIcon"><div class="m_4451eb3a mantine-Center-root"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-chevron-compact-right "><path d="M11 4l3 8l-3 8"></path></svg></div></div></span><span class="mantine-List-itemLabel"><button style="color:inherit;text-decoration:underline" class="mantine-focus-auto m_87cf2631 mantine-UnstyledButton-root" type="button" aria-label="Карьера">Карьера</button></span></div></li></ul></div><div style="justify-content:end;margin-top:0rem;position:sticky;top:calc(5rem * var(--mantine-scale))" class="m_8bffd616 mantine-Flex-root __m__-_R_5dlmiub_"><div tabindex="0" style="cursor:pointer"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses_web_development?promo_name=program_category&promo_position=blog_post&promo_creative=card&promo_type=card"><div style="background-color:var(--mantine-color-default);border:calc(0.0625rem * var(--mantine-scale)) solid var(--mantine-color-default-border);padding-inline:var(--mantine-spacing-xl);padding-top:var(--mantine-spacing-xl);padding-bottom:var(--mantine-spacing-xs);width:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root"><div class="m_4451eb3a mantine-Center-root" data-inline="true"><p style="font-size:var(--mantine-font-size-h4)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Курсы по веб-разработке</p></div><img class="m_9e117634 mantine-Image-root" src="/vite/assets/development-BVihs_d5.png"/><p style="margin-bottom:var(--mantine-spacing-xs);text-align:right" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></a></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>