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

Раньше я уже подходил к вопросу об эмпирической оценке сложности синтаксиса языков программирования, что называется «в лоб»: взять исходный код компилятора и посмотреть, сколько строк в нём занимает синтаксический анализатор. Ведь чем сложнее синтаксис, тем больше кода нужно, чтобы его распознать. И сотни тысяч строк кода только на анализ синтаксиса C++ - это измеримое свидетельство того, в какого монстра превратился C++ за сорок лет развития.

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

Два мира в одном языке

Когда программист пишет код на любом языке программирования, он работает с двумя принципиально разными группами понятий одновременно.

Первая группа понятий описывает - что делает программа - это различные бизнес-правила, прикладные и предметные сущности, которые используются для описания алгоритма. Например: class Order, enum Status { Paid, Pending }, if balance < amount: raise InsufficientFunds - всё это может прочитать и понять любой человек при первом взгляде на код, даже без знания синтаксиса конкретного языка.

Вторая группа понятий, которой оперирует программист, - это описание того, как это исполняется - управление памятью, диспетчеризация вызовов, синхронизация потоков и различные директивы компилятору. Это может быть alignas(64), volatile, reinterpret_cast, __slots__ - и здесь непрограммист уже беспомощен, и это нормально: эти конструкции адресованы компилятору (машине), а не человеку из предметной области.

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

Критерии оценки сложности синтаксиса ЯП

Может показаться, что отнесение языка программирования к категориям «низкоуровневый/высокоуровневый» или «системный/прикладной» - это сугубо экспертная оценка, причём довольно относительная. Например, C++ или Rust будут языками высокого уровня по сравнению с Ассемблером. Однако использование терминов «системный» или «прикладной» зависит даже не от языка, а от назначения конкретной программы.

Но если говорить именно про синтаксис, то можно взять полный список ключевых слов (включая встроенные аннотации) и задать по каждому из них всего два вопроса:

  1. Может ли эксперт предметной области, т. е. непрограммист, понять назначение этой конструкции без знания языка?

  2. Влияет ли эта конструкция языка на компилятор, рантайм или архитектуру железа?

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

C++

Полный список ключевых слов по стандарту C++20 - 92 слова, плюс разные операторы и стандартные атрибуты ([[nodiscard]], [[deprecated]], [[likely]] и др.), плюс break, continue. Всего 115 лексических конструкций, из которых:

  • Прикладные (говорят о предметной области) всего 34. Ключевые слова: bool, enum, enum class, const, struct, return, override, operator, if, else, switch, case, default, for, while, do, try, catch, throw, class, public, private, protected, co_await, co_yield, co_return, concept, requires, текстовые псевдонимы логических операторов: and, or, not (хотя они и не рекомендованы к использованию, но в лексике языка всё равно присутствуют) и несколько атрибутов: [[nodiscard]], [[deprecated]], [[fallthrough]].

  • Системные (говорят о компиляторе, памяти, железе) 81 из 115. Ключевые слова: int, short, long, float, double, char, wchar_t, char8_t, char16_t, char32_t, void, unsigned, signed и т. д. Текстовые псевдонимы побитовых и составных операторов: and_eq, or_eq, not_eq, xor, xor_eq, bitand, bitor, compl и различные атрибуты: [[maybe_unused]], [[likely]], [[unlikely]], [[noreturn]], [[carries_dependency]], [[no_unique_address]], [[optimize_for_synchronized]].

Итого прикладных терминов в синтаксисе C++ всего 34 из 115 (около 30%).

Python

Официальный список ключевых слов Python 3.12 - 35 слов, плюс 8 встроенных декораторов (@property, @staticmethod, @classmethod, @abstractmethod, @override, @dataclass, @cached_property, @functools.wraps). Итого 43 конструкции, из которых:

  • Прикладных 35 из 43. Это ключевые слова: True, False, None, def, return, lambda, yield, class, if, elif, else, for, while, try, except, raise, finally, with, import, from, as, async, await, del, not, and, or, is, in и декораторы: @property, @staticmethod, @classmethod, @abstractmethod, @override, @dataclass.

  • Системные ключевые слова Python: global, nonlocal, pass, break, continue, assert, type - всего 7 слов и два декоратора: @cached_property, @functools.wraps.

Итого прикладных терминов Python: 35 из 43 (более 81%)!

Rust

Официальный список ключевых слов Rust - 47 ключевых слов (из которых 9 зарезервированных) и 49 встроенных атрибутов, всего 96 терминов, из которых:

  • Прикладные ключевые слова: true, false, enum, const, let, struct, fn, return, trait, pub, where, if, else, match, for, in, async, await и атрибуты: #[derive(...)], #[must_use], #[deprecated], #[test] и т. д. - всего 26 прикладных терминов.

  • Системные ключевые слова: type, impl, dyn, Self, mut, ref, static, move, unsafe, extern, break, continue, super, as, loop, use, crate, mod, become, box, do, final, macro, override, priv, typeof, unsized, virtual, yield, abstract, alignof, offsetof, pure, sizeof - всего 29 ключевых слов, плюс 41 системный атрибут, таких как #[cfg(...)], #[cfg_attr(...)], #[inline] и т. д.

Итого прикладных терминов в Rust 26 из 96 (всего 27%).

В итоге прикладная часть синтаксиса ЯП выглядит так

Язык

Всего конструкций

Прикладных

Системных

% прикладных

Python

43

35

9

81%

Rust

96

26

70

27%

C++20

115

34

81

30%

C++26

121

37

84

31%

C++ по праву считается самым сложным. У него не только самый большой синтаксический словарь (121 конструкций против 43 у Python), но и очень неблагоприятное соотношение для прикладного пользователя: 84 системных термина против 34 прикладных. Программист на C++ вынужден держать в голове не только логику предметной области, но и управление памятью (new/delete, RAII, alignas, alignof), приведения типов (несколько разных *_cast), оптимизации компилятора (constexpr, consteval, constinit, inline, noexcept), диспетчеризацию (virtual, override, final) и метапрограммирование (template, typename, decltype). Ни один другой промышленный язык программирования не требует явного управления таким количеством системных деталей одновременно.

Доля прикладных терминов в грамматике Rust - 27%, это даже меньше, чем в C++. Но несмотря на это, с ним работать всё равно проще. А секрет заключается в удачном архитектурном решении: в Rust системные концепции не размазаны по ключевым словам, как в C++, а вынесены в атрибуты с однотипным синтаксисом. За счёт чего ключевые слова остаются относительно прикладными (trait, match, pub, where), а системные детали (#[repr], #[inline], #[cfg]) вынесены в отдельный синтаксический слой, который визуально легко отличим в исходном тексте программы.

Тем не менее Python всё равно не просто «проще» - его синтаксис специально ориентирован на предметную область значительно сильнее, чем C++ или Rust. И это не субъективное ощущение, а архитектурное свойство языка: из 43 конструкций 35 говорят о предметной области, и только 9 - о деталях рантайма. Поэтому if balance > 0 and status == "active" сможет понять любой человек даже без знания Python.

Почему Python выиграл в ML?

Оказывается, нет ничего удивительного в том, что Python предпочтительнее для использования в любых экспериментах и прототипах. Исследователь, пришедший из физики или биологии, читает:

for batch in dataloader:
    logits = classifier(batch.features)
    loss = cross_entropy(logits, batch.labels)
    loss.backward()
    optimizer.step()

Здесь каждое слово - из его профессионального словаря. for, loss, backward, step - это математика, записанная кодом. Системные детали (event loop, GIL, reference counting) не имеют здесь ключевых слов, потому что Python намеренно убрал их из синтаксиса.

И как бы выглядел аналогичный фрагмент на C++:
for (size_t i = 0; i < dataloader.size(); ++i) {
    const auto& batch = dataloader[i];
    
    // RAII-обёртка для выровненной памяти
    alignas(64) std::vector<float> logits;
    logits.reserve(num_classes);
    
    // Проверка на nullptr перед dynamic_cast
    if (auto* derived = dynamic_cast<DerivedClassifier*>(classifier.get())) {
        derived->forward(batch.features, logits);
    } else {
        throw std::runtime_error("Invalid classifier type");
    }
    
    // Вычисление функции потерь
    const float loss = cross_entropy(
        logits.data(), 
        batch.labels.data(),
        static_cast<size_t>(batch.labels.size())
    );
    
    // Обратное распространение с проверкой переполнения
    if (!std::isfinite(loss)) {
        throw std::overflow_error("Loss is infinite or NaN");
    }
    
    backward(loss, classifier->parameters());
    
    // Обновление весов с mutex для thread-safety
    {
        std::lock_guard<std::mutex> lock(optimizer_mutex);
        optimizer.step(classifier->parameters());
    }
}

C++ не спасает ничего: ни обобщённое программирование, ни умные указатели. В C++ доминирует низкоуровневый системный словарь: alignas, std::vector::reserve, dynamic_cast, static_cast, std::isfinite, std::lock_guard, std::mutex, try/catch, std::bad_alloc, std::bad_cast, std::exception, throw. Каждая строка требует явного управления типами (const auto&, size_t, float*), безопасностью памяти (даже RAII-обёртки), проверками корректности (nullptr, isfinite) и синхронизацией потоков (mutex, lock_guard).

Исследователь видит не математику обучения - он видит внутренности инфраструктуры выполнения. И это не недостаток C++: это запись тонкостей реализации, которые в Python скрыты за интерпретатором. Но это другой уровень языка - язык управления ресурсами и полного контроля над исполнением, но не предметной области.

Для честности, вот код Python с обработкой ошибок:

for batch in dataloader:
    try:
        logits = classifier(batch.features)
        loss = cross_entropy(logits, batch.labels)

        if not loss.isfinite():
            raise ValueError(f"Loss is not finite: {loss.item()}")

        loss.backward()
        optimizer.step()

    except RuntimeError as e:
        logger.error("Forward/backward pass failed: %s", e)
        classifier.zero_grad()
        raise
    except ValueError as e:
        logger.error("Invalid loss value: %s", e)
        raise

И даже в этом случае Python остаётся в прикладном словаре: loss, backward, step, isfinite - всё это математика и предметная область. Системные детали (RuntimeError, zero_grad) появляются только в блоках except - то есть в исключительных ситуациях, а не в основном потоке логики. Тогда как в C++ низкоуровневые системные термины (std::isfinite, std::lock_guard, dynamic_cast, static_cast) стоят прямо в основном потоке, рядом с loss и backward.

Немного другая ситуация получается с программой на Rust:

for batch in &dataloader {
    let logits = classifier.forward(&batch.features)?;
    let loss = cross_entropy(&logits, &batch.labels)?;

    if !loss.is_finite() {
        return Err(TrainingError::InvalidLoss(loss));
    }

    loss.backward()?;
    optimizer.step(classifier.parameters())?;
}

Данный вариант читается почти так же легко, как Python - for, loss, backward, step остаются на месте. Но системный словарь всё равно просачивается: & перед каждым аргументом - это явное заимствование, которое borrow checker требует обозначать всегда. ? после каждого вызова - это явная обработка Result<T, E>, которую нельзя проигнорировать молча. И практически любой специалист предметной области (т. е. непрофессиональный программист) споткнётся именно здесь: не на логике машинного обучения, а на том, почему нельзя написать просто classifier.forward(batch.features).

Тот же самый код Rust с явным управлением владением, синхронизацией и обработкой ошибок:
for batch in dataloader.iter() {
    // Явное заимствование: borrow checker требует знать,
    // кто владеет данными и на какой срок
    let logits = match classifier.forward(&batch.features) {
        Ok(output) => output,
        Err(e) => {
            eprintln!("Forward pass failed: {}", e);
            classifier.reset_gradients();
            return Err(TrainingError::ForwardFailed(e));
        }
    };

    let loss = match cross_entropy(&logits, &batch.labels) {
        Ok(l) if l.is_finite() => l,
        Ok(l) => {
            return Err(TrainingError::InvalidLoss(l));
        }
        Err(e) => return Err(TrainingError::LossFailed(e)),
    };

    // backward() потребляет loss - владение передаётся,
    // после этой строки loss использовать нельзя
    loss.backward().map_err(|e| {
        classifier.reset_gradients();
        TrainingError::BackwardFailed(e)
    })?;

    // Обновление весов через Mutex - явная синхронизация,
    // lock живёт ровно до конца блока (RAII)
    {
        let mut params = classifier
            .parameters()
            .lock()
            .map_err(|_| TrainingError::PoisonedMutex)?;

        optimizer.step(&mut params).map_err(|e| {
            TrainingError::OptimizerFailed(e)
        })?;
    } // lock освобождается здесь автоматически
}

В Rust каждая конструкция несёт конкретную системную гарантию, но за это Rust платит тем, что его системный словарь виден даже в прикладном коде (точно так же, как и в случае с C++). И это происходит везде, даже в мелочах. Самая простая операция - проверка конечности loss:

# Python: скрыто за исключением фреймворка
loss.backward()
// C++: явная проверка + явное исключение + явный тип
if (!std::isfinite(loss)) {
    throw std::overflow_error("Loss is infinite or NaN");
}
// Rust: компилятор требует обработать Result явно
if !loss.is_finite() {
    return Err(TrainingError::InvalidLoss(loss));
}

В Python не нужно ничего проверять - фреймворк сделает это сам или упадёт с понятным сообщением, тогда как C++ требует явной проверки, потому что арифметика с double не бросает исключений по стандарту, и это знание о внутреннем устройстве числового представления, а не о задаче. Rust идёт ещё дальше: он запрещает проигнорировать ошибку на уровне системы типов.

Именно в таких деталях и живёт разница между 80% прикладных конструкций у Python и всеми остальными языками: не в синтаксическом сахаре и не в длине кода, а в том, сколько системных знаний нужно держать в голове, чтобы написать казалось бы, очень простую вещь.

Все языки эволюционируют в сторону предметной области

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

Особенно сильно это заметно в C++ - но с парадоксальным результатом. C++20 принёс concept и requires - конструкции, которые говорят о намерении, а не о механизме. C++23 добавил std::expected. C++26 делает самый смелый шаг: статическая рефлексия через std::meta убирает макросы и кодогенераторы, контракты pre/post превращают бизнес-ограничения в часть сигнатуры функции, std::execution прячет асинхронность за читаемым пайплайном.

double sqrt(double x)
    pre(x >= 0.0)
    post(result: result * result <= x);

Это поймет любой человек с математическим образованием. Но те же контракты имеют несколько режимов проверки, взаимодействуют с noexcept и по-особому ведут себя при наследовании. std::meta требует понимания constexpr-вычислений и нового оператора ^. std::execution вводит пять новых абстракций: sender, receiver, scheduler, operation state, completion signatures.

Вот в чём парадокс: каждая прикладная добавка тянет за собой новый слой системных концепций. Когда Python добавляет @dataclass, новичок пользуется им на следующий день. Когда C++26 добавляет std::meta, между «понял идею» и «применяю правильно» лежат недели. Язык становится выразительнее для тех, кто уже знает его глубоко - и сложнее для всех остальных. Это не провал комитета, а честная цена универсальности: язык, который одновременно управляет байтами и выражает бизнес-контракты, не может быть простым по определению.

Короче, выглядит это как-то так:

Итог

Споры «Python vs C++» или «C++ vs Rust» традиционно ведутся в терминах производительности, безопасности типов или размера экосистемы. Но есть ещё одно измерение, которое редко называют явно: для кого разработан синтаксис языка?

Когда Python-код читает непрограммист и понимает его без погружения в детали рантайма - это не случайность, а результат дизайна языка. Когда C++ программист пишет [[nodiscard]] constexpr auto compute() noexcept - каждое слово адресовано компилятору. Это не плохо: это честная запись системных гарантий, но это не прикладной, а системный словарь.

Python выиграл в ML потому, что думает на том же языке, на котором думают исследователи. А это, как выясняется, вполне конкретно и даже поддаётся измерению. Python структурно ориентирован на предметную область почти втрое сильнее, чем C++ или Rust, и это не просто «синтаксический сахар», а принципиальная архитектурная разница языков, и именно поэтому ни C++, ни Rust не смогут заменить «медленный» Python, который позволяет очень быстро делать прикладные вещи.

Комментарии (39)


  1. shaseer
    28.07.2026 07:18

    Python структурно ориентирован на предметную область почти втрое сильнее, чем C++ или Rust

    Ориентированность на предметную область определяется только количеством ключевых слов? Тогда по ориентированности условный Brainfuck оставляет Python далеко позади


    1. sergey_vasin
      28.07.2026 07:18

      Или язык с единственным оператоом “сделай все” ;)


  1. Apoheliy
    28.07.2026 07:18

    Это только меня напрягает:

    Python 43 35 9 81%

    ???

    Как-бы: 9 + 35 = 44

    Сарказм: может поэтому питон и нравится автору: подогнать можно под любой ответ!?


    1. rsashka Автор
      28.07.2026 07:18

      Спасибо, что заметили опечатку.


  1. steelfactor
    28.07.2026 07:18

    Какая "глубокая по силе" статья! Браво, автор, взять списки ключевых слов, произвольно поделить их на «прикладные» и «системные», получить красивые проценты, из чего сделать вывод, что Python «структурно ориентирован на предметную область почти втрое сильнее» и поэтому «никогда» не будет заменён.
    Приплести сюда реальность ML-стека - мать моя женщина...

    Практически весь производительный код современного ML написан не на Python:

    -Ядро PyTorch (ATen, CUDA-ядра) - C++/CUDA.

    -NumPy - по большей части C.

    -Tokenizers, safetensors, многие высокопроизводительные компоненты Hugging Face - всё чаще Rust.

    -Production inference, high-frequency и низколатентные системы - C++/Rust/CUDA.

    Python выигрывает как язык-клей и язык прототипирования, а не как язык, на котором реально крутится тяжёлая математика. Это классическая проблема двуязычия, которую пытаются решать Julia, Mojo, Swift for TensorFlow и т.д. И делать вид, будто её не существует - очень некрасиво по отношению к читателю.
    Утверждать, что C++ или Rust «никогда не смогут заменить Python» это всё равно что утверждать, что Python «никогда не сможет заменить C++ в ядрах ОС или драйверах».
    Бред короче


    1. rsashka Автор
      28.07.2026 07:18

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

      Утверждать, что C++ или Rust «никогда не смогут заменить Python» это всё равно что утверждать, что Python «никогда не сможет заменить C++ в ядрах ОС или драйверах».

      В ядрах ОС этот самый С++ обычно не используется


  1. shaseer
    28.07.2026 07:18

    Язык - это инструмент. Если инструмент 1 лучше подходит для работ по металлу, а инструмент 2 - для работ по дереву, то резать доски можно и первым, но зачем?


  1. tenzink
    28.07.2026 07:18

    При понятном посыле статьи, примеры на C++/Rust притянуты за уши. Раз уж тут псевдокод, то ловите функциональный аналог питоновской версии на C++

    for( const auto& batch : dataloader) {
        auto logits = classifier(batch.features);
        auto loss = cross_entropy(logits, batch.labels);
        loss.backward();
        optimizer.step();
    }

    и на Rust

      for batch in dataloader {
          let logits = classifier.forward(&batch.features)?;
          let loss = cross_entropy(&logits, &batch.labels)?;
          /// ...
      }



    1. misha_erementchouk
      28.07.2026 07:18

      Тоже глаза резануло. В результате главный тезис статьи очень сильно проседает. А если еще, в С++ и Rust, позволить программе паниковать если какой-то функции ее собственный результат не понравился, так и вообще мало, что остается.


    1. rsashka Автор
      28.07.2026 07:18

      Весь псевдокод предполагает некоторые допущения. В вашем случае, допущением является отсутствие необходимости в обработке ошибок в коде на С++, тогда как обработка ошибок в Python уже реализована в вызываемых инструментах. Тоже самое и с Rust. Что будет в случае возникновения ошибки?


      1. tenzink
        28.07.2026 07:18

        И в Rust, и в C++ ничего дополнительно делать не нужно. Уже в псевдокоде всё работает "из коробки" - ошибки уже корректно обрабатываются:


    1. Granulex
      28.07.2026 07:18

      Примеры красивые – правда, `///` в Rust это doc-комментарий, и компилятор споткнётся о него раньше, чем дойдёт до обучения. По-моему, отличная иллюстрация к самой статье: в Python оборванный цикл просто отработает, а тут вы сначала полчаса объясняете компилятору, что вообще имели в виду. Краткость записи и краткость пути до результата – это разные краткости.


      1. tenzink
        28.07.2026 07:18

        В том-то и дело, что полный код и на Rust, и на C++ будет безусловно сложнее, но не там, где ищет автор статьи.


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


        1. rsashka Автор
          28.07.2026 07:18

          Вы правы. Просто в случае С++ вы используете библиотеку с помощью её внутреннего API и всех возможностей зыка разработки. Тогда как в случае Python, разработчик обязан адаптировать внешний API библиотеки к возможностям целевого языка, т.е. сделать его достаточно простым. А это в свою очередь и определяется возможностями синтаксиса Python.


  1. sergey_vasin
    28.07.2026 07:18

    У С++ ключевая фишка - сохранение обратной совместимости


  1. punzik
    28.07.2026 07:18

    Как по мне, так на математику это совсем не похоже

    for batch in dataloader:
        logits = classifier(batch.features)
        loss = cross_entropy(logits, batch.labels)
        loss.backward()
        optimizer.step()
    

    Особенно последние две строчки. Мутабельный loss? Optimizer на каких-то скрытых сайд-эффектах и глобальных переменных? Совершенно непрозрачно.

    А по поводу простоты синтаксиса и количества ключевых слов можно вспомнить Scheme. Там нет ни синтаксиса ни ключевых слов, и выглядит он гораздо понятней и математичней.


    1. rsashka Автор
      28.07.2026 07:18

      Чуть выше уже вспомнили Brainfuck и есть еще универсальная машина Тьюринга с её лентой и простейшей программой на переходах. Осталось только с их помощью написать что нибудь полезное.


      1. punzik
        28.07.2026 07:18

        Ну сравнивать Brainfuck и машину Тьюринга с языком, по которому 40 лет преподавался курс CS в MIT-е - такое себе.


  1. koulikoff
    28.07.2026 07:18

    Питон может быть и будет заменён ruby. Потому, что

    • руби более простой язык для программиста.

    • он быстрее в исполнеии

    • он имеет прекрасные средства не только тестирования, но и одновременно документации кода.

    • разработка на нём примерно в 2 раза быстрее, чем на питоне.

    Проблема только в том, что преподаватели в школах выучили python и не выучили ruby, поэтому они обучают первому языку, а не второму.


    1. rsashka Автор
      28.07.2026 07:18

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


  1. Plesser
    28.07.2026 07:18

    Рискую сейчас обнулить свою карму, но все таки

    Зачем смотреть на языки программирования с точки зрения что кто то должен кого то заменить?
    если вам надо перевезти кучу мебели вы арендуете фургон или малолитражку? каждый язык предназначен для решений своих задач. Вы бы еще написали статью заменит ли когда нибудь Linux Windows :)


    1. rsashka Автор
      28.07.2026 07:18

      Если у вас в фургоне панель управления как в самолете, то может быть взять малолитражку и съездить несколько раз с привычнымии органами управления, чем сперва учиться, потом сдавать на права и привезти всю мебель за один раз, но только через 4 месяца?


      1. Plesser
        28.07.2026 07:18

        если у вас панель управления как в самолете то может лучше нанять специалиста (водителя фургона) что бы он это сделал?


        1. rsashka Автор
          28.07.2026 07:18

          Вы, как исследователь, наверно сможете нанять водителя программиста, чтобы он вам написал программу на C++. Но ему во первых нужно платить, а во вторых, вы еще сами не знаете, куда нужно ехат что конкретно нужно делать.


          1. Plesser
            28.07.2026 07:18

            Ну если вы не знаете что надо делать, то может не надо это делать?

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


            1. rsashka Автор
              28.07.2026 07:18

              Ну если вы не знаете что надо делать, то может не надо это делать?

              Это вопрос не ко мне, а к ученым, исследователям и всем тем, что пытается изучить или сделать что-то новое. Или вы сами никогда не сталкивались с задачами, которые неизвестно как решать?


              1. Plesser
                28.07.2026 07:18

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


                1. rsashka Автор
                  28.07.2026 07:18

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


                  1. Plesser
                    28.07.2026 07:18

                    Ну вот я решил этой зимой запрограммировать роботележку на esp32. Я большую часть своей проф жизни программировал pl/sql и не много python. Почитал интернеты, сделал прототип с начало на С с использованием фреймворка Arduino. Тележка хреново но поехала, потом стал углубляться в FreeRTOS и стал туда добавлять ее возможности. Тележка поехала уже намного лучше. Побочный эффект смог уже сам добавить туда ультразвуковой сонар. На очереди лидар и esp32-cam. Жизнь прекрасна :)


  1. v_0ver
    28.07.2026 07:18

    Всё же Python это альтернатива Matlab/Maple итд, а не С++/Rust . Python в мире ML это язык скриптования тулбоксов: PyTorch, JAX, итд. А С++/Rust это технологи на которых реализуют эти тулбоксы.


  1. v_0ver
    28.07.2026 07:18

    Пример “хорошего” api как раз и показывает компромиссность связанную с производительностью Python:

    for batch in dataloader:
        logits = classifier(batch.features)
        loss = cross_entropy(logits, batch.labels)
        loss.backward()
        optimizer.step()
    

    Где градиент? Какие параметры оптимизирует оптимизатор? Как эти параметры попадают в целевую функцию? Совершенно не видно потока данных. Всё это скрыто не потому, что это удобно - это не удобно, а потому, что мы не хотим, чтобы выполнение вываливалось в python-код и тормозило библиотечный код.


    1. rsashka Автор
      28.07.2026 07:18

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

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


  1. Siemargl
    28.07.2026 07:18

    А почему не Julia ?


    1. rsashka Автор
      28.07.2026 07:18

      Как по мне, Julia - это не промышленный язык программирования, а пример коммерческого языка для академической среды, который развивается на гранды.


  1. Siemargl
    28.07.2026 07:18

    По-моему логическая цепочка проще:

    1. С++ сложный -> с него не начинают учить программированию

    2. Python учат в школе (колледже) и его многим достаточно -> его используют для всего [непрофессионалы в программировании]


    1. rsashka Автор
      28.07.2026 07:18

      Вы правы. Просто вопрос в том, что является причиной оценки “простой” или “сложный”. И мне захотелось посмотреть на эту проблему с описанной в статье точки зрения. Ведь если словарь языка программирования без затруднения понимается не программистом, то он априори будет “проще”.


    1. Plesser
      28.07.2026 07:18

      у питона легкий вход но затем кривая обучения резко вырастает, у си++ наоборот, с начало тяжело потом легко.
      по мне очень жаль что начинают учить не с С/С++ а с Python. Ломать мозги при переходе с ЯП где динамические типа на ЯП со статическими типами больно (проходил это когда переучивался с Basic на C в начале 90-х)


      1. rsashka Автор
        28.07.2026 07:18

        Как по мне, то у C++ легко не бывает в принципе :-(


        1. Plesser
          28.07.2026 07:18

          мы рождены что бы страдать )))))