Надоел JavaScript — используй браузерный Python

Автор оригинала: Yakko Majuri
Мой опыт разработки игры «Змейка» на Brython

image

«Погоди, что?» – думаю, большинство читателей отреагирует на заголовок именно так.

В смысле «просто использовать Python в браузере»?

Все знают, что в браузерах работает только JavaScript.

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

Да, это Python!

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

Знакомство с Brython


Brython — это реализация Python3, написанная на JavaScript, которая позволяет писать код на Python для веба.

По сути, это JavaScript-библиотека, которая преобразует ваш код на Python в эквивалентный JS и исполняет его в рантайме.

Поскольку написание браузерного кода на Python звучит круто, я решил попробовать.

Разработка «Змейки» на Brython


image

Вот ссылка на мой сайт, где вы можете попробовать версии «Змейки» на JavaScript и Brython. А вот ссылка на GitHub c исходным кодом.

Для того, чтобы опробовать Brython, я решил написать классическую «Змейку».

Так как я не специалист по работе с Canvas в HTML и не разработчик игр, я решил использовать эту JavaScript-реализацию в качестве отправной точки. Когда-то я уже создавал свою «Змейку» на основе Canvas, но эта реализация более аккуратная и компактная.

А еще автор написал ее менее чем за 5 минут. Надо отдать должное Крису Делеону, это очень впечатляет.

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

Также я модифицировал его код, чтобы он работал в режиме strict, так как в реализации Криса используются вещи вроде неявных глобальных переменных, которые, на мой взгляд, не отражают того, как выглядит большая часть кода на JS (не критикую автора – он программировал на время). Я хотел получить хорошее сравнение кода на Brython и JS.

JavaScript оказался таким, и я не буду размещать этот код здесь, потому наша цель – сфокусироваться на Brython.

Несмотря на то, что большая часть кода на Brython была «дословным переводом» с JS, некоторые части (например, функционал подсчета очков) были написаны непосредственно на Brython, а затем реализованы на JS – чтобы посмотреть на отличия.

Окончательный результат выглядит следующим образом:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Brython Snake</title>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.8.9/brython.min.js">
    </script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <style> /* Removed to keep the snippet short. Find the full file here: */ </style>
</head>

<body onload="brython()">

    <h1 class="text-center">Snake built with <a href="https://brython.info">Python!</a></h1>
    <canvas id="game-board" width="400" height="400"></canvas>
    <br>
    <h3 id="score" class="text-center">Score: 0</h3>
    <br>
    <h6 id="high-score" class="text-center">High Score: 0</h6>
    <br>
    <div class="text-center">
        <button id="instructions-btn" class="btn btn-info">Instructions</button>
    </div>

    <script type="text/python">
        
        from browser import document, html, window
        from javascript import Math
        
        score = 0
        high_score = 0

        px = py = 10
        gs = tc = 20
        ax = ay = 15
        xv = yv = 0
        trail = []
        tail = 5

        pre_pause = [0,0]
        paused = False
   
        def game():
            global px, py, tc, gs, ax, ay, trail, tail, score
            px += xv
            py += yv
            if px < 0:
                px = tc-1
            if px > tc-1:
                px = 0
            if py < 0:
                py = tc-1
            if py > tc-1:
                py = 0
            ctx.fillStyle = "black"
            ctx.fillRect(0, 0, canvas.width, canvas.height)
            ctx.fillStyle = "lime"
            for i in range(len(trail)):
                ctx.fillRect(trail[i][0]*gs, trail[i][1]*gs, gs-2, gs-2)
                if trail[i][0] == px and trail[i][1] == py:
                    score = score if paused else 0 
                    tail = 5
            trail.insert(0, [px, py])
            while len(trail) > tail:
                trail.pop()
        
            if ax == px and ay == py:
                tail += 1
                ax = Math.floor(Math.random()*tc)
                ay = Math.floor(Math.random()*tc)
                score += 1
            update_score(score)
            ctx.fillStyle = "red"
            ctx.fillRect(ax*gs, ay*gs, gs-2, gs-2)
        
        def update_score(new_score):
            global high_score
            document["score"].innerHTML = "Score: " + str(new_score)
            if new_score > high_score:
                document["high-score"].innerHTML = "High Score: " + str(new_score)
                high_score = new_score

        def key_push(evt):
            global xv, yv, pre_pause, paused
            key = evt.keyCode
            if key == 37 and not paused:
                xv = -1
                yv = 0
            elif key == 38 and not paused:
                xv = 0
                yv = -1
            elif key == 39 and not paused:
                xv = 1
                yv = 0
            elif key == 40 and not paused:
                xv = 0
                yv = 1
            elif key == 32:
                temp = [xv, yv]
                xv = pre_pause[0]
                yv = pre_pause[1]
                pre_pause = [*temp]
                paused = not paused
            
        def show_instructions(evt):
            window.alert("Use the arrow keys to move and press spacebar to pause the game.")
        
        canvas = document["game-board"]
        ctx = canvas.getContext("2d")
        document.addEventListener("keydown", key_push)
        game_loop = window.setInterval(game, 1000/15)
        instructions_btn = document["instructions-btn"]
        instructions_btn.addEventListener("click", show_instructions)
    
</script>

</body>

</html>

Итак, основываясь на этом фрагменте, давайте разберемся в базовых понятиях Brython

Подключение brython.js


Для использования Brython не требуется установка. Просто импортируйте скрипт внутри head :

<script type=”text/javascript” src=”https://cdn.jsdelivr.net/npm/brython@3.8.9/brython.min.js">

Запуск Brython


Для того, чтобы Brython мог транслировать и исполнять код на Python так, будто это код на JS, нам нужно вызвать Brython как раз тогда, когда загрузится тело документа. Например, так:

<body onload=”brython()”>

Этот тег будет выполнять поиск тегов script c типом "text/python" и запускать их код.

API для работы с вебом


JavaScript по умолчанию дает доступ к объектам вроде document и window, необходимым в любом JS-проекте. Соответственно, Brython тоже должен иметь возможность работать с ними.

Для решения этой проблемы создатели Brython могли бы просто дать разработчикам возможность обращаться к этим объектам из кода на Python, но это привело бы к крикам дебаггеров о undefined variable и снижению производительности.

Таким образом, чтобы использовать эти API, мы должны импортировать их точно так же, как импортируем любой другой модуль на Python:

from browser import document, html, window

И вам не нужно выполнять команду pip install. В конце концов, вы вставляете все это в HTML! Просто добавьте требуемые импорты, с остальным разберется Brython.

Чтобы увидеть насколько хорошо все это работает, я попробовал использовать несколько различных методов из Web API: alert, setInterval, addEventListener и т.д. Все они сработали так, как должны были.

Встроенные объекты и методы JavaScript


В «Змейке», как только змея съест яблоко, нам нужно сгенерировать новое яблоко в случайном месте.

Однако, я не могу использовать модуль random из библиотеки Python*. Так как же я могу сгенерировать случайное число (без написания собственной библиотеки)?

Оказалось, что в Brython более широкая поддержка JavaScript, чем я думал. Смотрите:

from javascript import Math
random_num = Math.floor(Math.random()*10)

Благодаря модулю javascript, если есть объект, к которому я могу получить доступ с помощью JS, то я могу получить к нему доступ с помощью Brython.

Если я импортирую JavaScript-библиотеку (jQuery, Bootstrap) и захочу использовать ее методы — я могу сделать это с помощью from javascript import <библиотека>. И, естественно, я также могу использовать встроенные JS-объекты, например, Date или String.
*По всей видимости, Brython поставляется с рядом стандартных библиотек Python, реализованных непосредственно на JavaScript, и если у какого-то модуля нет JS-версии, то вы все равно сможете импортировать его. Brython получит версию на чистом Python и код импортированного модуля будет работать вместе с кодом Brython. Впрочем, модуль random у меня не заработал – но я могу понять, почему.

Специфические конструкции


В Python, если я хочу распаковать список, я могу написать list2 = [*list1]. Также, если я хочу присвоить переменной значения исходя из некоторого условия, я могу написать foo = 10 if condition else 20.

У этих конструкций есть эквиваленты в JavaScript: оператор spread ( [...arr] ) и тернарный оператор ( let foo = condition ? 10 : 20 ).

Но поддерживает ли их Brython?

Я попробовал их, и они отлично сработали. Вы можете увидеть, что распаковка списка из Python и условное присваивание используются в моем коде.

Отладка


Честно говоря, я думал, что отладка в Brython будет ужасной.

На самом деле все не так уж и плохо.

Конечно, я написал совсем небольшой и не очень сложный проект, но ошибки, брошенные Brython были в основном точными и довольно понятными.

Это верно, по крайней мере, в отношении синтаксических ошибок. Импорт модулей из библиотеки Python – совсем другая история.

Производительность


image

JavaScript Snake

image

Brython Snake

Как и ожидалось, код на Brython работает медленнее, чем JavaScript. В моем случае он был примерно в 1.7 раз медленнее.

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

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

Я действительно пытался использовать редактор Brython Editor для преобразования моего кода на Brython в JS и запуска получившегося кода на веб-странице, но из-за огромного количества ошибок я пока от этого отказался. Впрочем, я приложил к этому не слишком много усилий.

Заключительные мысли о Brython


Честно говоря, я был весьма впечатлен Brython. Вот несколько плюсов и минусов из моего собственного опыта работы с языком:

Плюсы

  • Мне удалось написать «Змейку» без лишних хлопот, причем опыт отладки оказался на удивление положительным.
  • В моем простом проекте Brython беспрепятственно взаимодействовал с нативными JavaScript-объектами, доступными на странице
  • Я ценю тот факт, что мой код выглядит чище на Python, а также мне нравится, что я могу использовать полезные конструкции из Python для написания браузерного кода.
  • В случае с моей игрой, хоть Brython загружается медленнее, чем JavaScript, пользователь этой разницы не замечает.
  • Мне приятно видеть Python в исходном коде моего сайта.

Минусы

  • Брайтон работает значительно медленнее, чем чистый JS.
  • Для использования Brython разработчику необходимо иметь опыт работы с JavaScript.
  • Вы неизбежно столкнетесь с большим количеством ошибок
  • Документации Brython и его сайту есть куда расти в плане удобства навигации и возможностей обучения
  • У Brython отсутствует сильная экосистема и инструменты развития.

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

Тем не менее, я считаю, что в настоящее время Brython больше подходит для JavaScript-разработчиков, знакомых с Python и уставших от JS, а не для Python-разработчиков, которые хотят заниматься веб-разработкой, не изучая JavaScript.

Я думаю, что понимание JavaScript необходимо для того, чтобы хорошо работать с Brython. И если вы решите потратить время на изучение JavaScript, чтобы вам было легче писать на Brython, то вы можете просто использовать JavaScript.

Другие альтернативы JS в браузере


image

Причина, по которой я выбрал Brython, заключалась в том, что из большинства вариантов перехода от Python к JS, о которых я впервые узнал, он был единственным, у кого ведется активная разработка на GitHub. Большинство просмотренных мною транспайлеров из Python в JavaScript не имеют коммитов в течение нескольких лет.

Впрочем, есть и другие альтернативы.

Pyodide, например, кажется интересным вариантом. Он компилирует Python (вместе с его научными библиотеками) в WebAssembly, что позволяет ему запускаться в браузере.

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

Таким образом, можно написать компилятор, который будет переводить Python (или любой другой язык) в WebAssembly, что позволит ему работать в браузере.

Это амбициозный и перспективный проект, который, скорее всего, приведет к тому, что мы увидим все больше веб-разработок без JavaScript.

Тем не менее, он все еще находится в зачаточном состоянии (~3 года), так что, вероятно, потребуется некоторое время, прежде чем мы увидим, что JavaScript регулярно заменяется другими языками.

И пока мы этого ждем, вам придется воспользоваться инструментами вроде Brython, если вы действительно не можете иметь дело с JavaScript.

Но, честно говоря, это неплохое начало!

image

Узнайте подробности, как получить востребованную профессию с нуля или Level Up по навыкам и зарплате, пройдя платные онлайн-курсы SkillFactory:



Читать еще


SkillFactory
Онлайн-школа по программированию

Комментарии 46

    +5
    Это с какой же скоростью оно работает? И сколько гигабайт памяти жрёт?
      –17
      Ваш вопрос про гигабайты имеет под собой основание?
        +12
        А Вы считаете, что нет? У Вас браузер кушает как птичка?
          –2
          Птичка, но не синичка. Обычно в состоянии индюшки, но может и до страуса располнеть.
      +6
      Э… А может, лучше всё-таки так не делать? Не зря люди через боль сваливают с JavaScript на TypeScript.

      В смысле, раз у вас не три строчки кода и понадобился язык поудобнее JavaScript – то перед запихиванием кода на страницу надо как минимум прогнать его через линтер или чем там принято проверять питоновский код. Заодно можно и транспиляцию выполнить – возможно, оптимизировав. И на сайте уже будет самый что ни на есть JavaScript.

      Хотя сам факт существования такой либы – это нереально круто.
      • НЛО прилетело и опубликовало эту надпись здесь
          +13
          Все знают, что в браузерах работает только JavaScript.

          Земля пухом VBScript-у.
            0

            А на самом деле Python в вебе куда приятнее был бы чем JavaScript

              +36
              И всем пришлось бы держать по два браузера: на одном работали бы сайты, написанные под Python 2.x, а на втором — под Python 3.
                +9

                "text/python" и "text/python3"?

                  0
                  Второй python уже как пол года deprecated
                +8
                А зачем это всё? Это для тех, кто очень хочет в веб разработку, но знает только синтаксис пайтона? ))
                  +1
                  Например, чтобы использовать модули, уже написанные на Python.
                    +5
                    То есть тормозов браузера мало? Мало скайпа, электронов и прочего, надо еще слой абстракции… пока он медленней всего в 1.7 раза, но мы поднапряжемся и напишем на питоне интерпретатор и засунем его в js.
                      0

                      А их скомпилировать нельзя?

                        0
                        Чем, например?
                        +2
                        Особенно интересно, когда модули захотят из C++ что-то собрать, в WASM это засунуть?
                      +1
                      давайте разберемся

                      Давайте:


                      import html

                      Вот это вот, например — зачем?

                        0
                        JavaScript по умолчанию дает доступ к объектам вроде document и window, необходимым в любом JS-проекте. Соответственно, Brython тоже должен иметь возможность работать с ними.
                        Для решения этой проблемы создатели Brython могли бы просто дать разработчикам возможность обращаться к этим объектам из кода на Python, но это привело бы к крикам дебаггеров о undefined variable и снижению производительности.
                        Таким образом, чтобы использовать эти API, мы должны импортировать их точно так же, как импортируем любой другой модуль на Python:
                          0
                          Я думаю, justhabrauser намекает на то, что импортируемый объект html дальше в коде не используется.
                            –2

                            Вы совершенно правы — именно на это он и намекает
                            (несмотря на отсутствие медали "программист Python" в профиле).
                            Даже на внезапно выскочившие ctx и canvas в game() недонамекнул.
                            Постеснялся, видимо.

                        0

                        Рекомендую вам почитать про уже достаточно зрелый WebAssember (WASM). Его сделали как раз, чтобы не делать вот такие, не очень оптимизированные и медленные инструменты.

                          0
                          И что, существует транслятор из Python в WASM?
                            0

                            Не знаю конкретно за питон, но для c/c++/c# они уже есть и неплохо работают. Я например уже использовал Blazor (c#), разворачивающий в браузере Mono VM на которой исполняются dll, ки. С питоном такое по идее сложнее сделать, т.к питон требует наличие операционной системы, но думаю это только вопрос времени

                          +12
                          Я засунул тебе в скриптовый язык скриптовый язык, чтобы ты писал скрипты, пока пишешь скрипты.

                          Моё мнение по теме — очень надеюсь, что WA станет стандартом и JS умрет и мы его будем вспоминать как страшный сон.
                            0
                            А что такое WA? Я не смог пересилить гугл и найти это. =)
                            +1
                            WA станет стандартом и JS умрет

                            WA станет стандартом, а JS не умрёт, помяните мои слова.
                            +5

                            Странно, что не упомянули RustPython, который является обычной питоновской ВМ, которую можно легко скомпилировать в васм из-за природы языка раст. Пощупать онлайн можно по адресу https://rustpython.github.io/demo/.

                              –1

                              При большом упорстве — CPython собирается аналогичным образом. Недавно встраивал в Golang приложение возможность сериализовать python объекты с помощью shared-library.


                              Любите же вы делать троллейбус…

                              0
                              Честно говоря, я думал, что отладка в Brython будет ужасной.
                              … но ошибки, брошенные Brython были в основном точными и довольно понятными.

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

                                +4

                                Для тех, кто не попал в эпоху, когда нтерактивные отладчики просто не существовали — именно так. Для всех остальных — это таки просто поиск ошибки. В частности, в вебе приходилось отлаживать alert-ами, а косяки в dom-модели искать, закрашивая все в округе разными бэкгнаундами :)

                                  –3

                                  Уже больше 10 лет есть возможность на вебе использовать интерактивные отладчики — Отладка Javascript. Оператор debugger появился в спецификации ECMAScript 5.1 (июнь 2011). Поэтому мне удивительно читать, что в 2020-м году отладка в web-приложениях идёт через сообщения об ошибках.

                                    0
                                    На серверной стороне тоже будете пошагово отлаживать?
                                      0

                                      Если nodejs — то да. Хотя в этом редко есть потребность.

                                        0

                                        Не буду, а отлаживаю. Когда нужно, разумеется.


                                        И без разницы, что на серверной стороне — node, PHP, python, C# или Java. Бэк так же хорошо отлаживается через отладчик, как и фронт. А с учётом абсолютной несвязанности по коду фронта и бэка (только по данным) каждую часть можно смотреть под дебаггером сколь угодно долго, даже если вторая часть отвалилась по timeout'у.


                                        Возможно, тут кто-то называет отладкой обычное логирование, ну так это разные вещи.

                                  +2
                                  Когда-то я писал нечто на вот этом — opalrb.com
                                  Это Ruby в браузере. И оно даже работало у меня в продакшене. Но лучше не надо.

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

                                  А так вообще были реализации полноценных виртуальных машин на JS, в которой можно запустить Linux, в которой можно запустить нужный язык или открыть браузер… нувыпонели. Но по продакшену всё же вопросы.
                                    0
                                    Если вам дико не хватает питон-style во фронте
                                    попробуйте coffee script
                                    Не пробуйте, он давно уже мертв
                                      0
                                      Coffee script это же ruby style.
                                      Собственно на рельсах он особенно и прижился
                                      0

                                      толку питона в браузере "без батареек", лучше тогда уж lua: https://fengari.io/

                                        0
                                        а внутрь питона жаву насувать, а к жаве библиотечки на докере на С и потом уж можно лендос «HELLO WORLD» запилить.
                                          0

                                          -"интерпретация интерпретируемого языка в js?)"

                                            –2
                                            а зачем? кто это говно будет поддерживать?
                                              0

                                              Сброс длины змейки и счета при нажатии стрелки противоположной направлению движения (вверх-вниз или вправо-влево) — это баг или фича?

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

                                              Самое читаемое