Надоело использовать Javascript для web? Вместо этого используйте браузерный Python

0
1584
Надоело использовать Javascript для web? Вместо этого используйте браузерный Python.
Надоело использовать Javascript для web? Вместо этого используйте браузерный Python.

«Подождите, что?» — вот как, по моему мнению, большинство людей отреагирует на название этой статьи.
Что вы имеете в виду, говоря «Просто используйте браузерный Python»?
Все знают, что браузеры могут выполнять только JavaScript.
Ниже приведен скриншот исходного кода моего личного сайта, посмотрите, заметите ли вы что-то другое:

Надоело использовать Javascript для web? Вместо этого используйте браузерный Python

Да, это Python!

А теперь давайте поговорим о том, как это работает, насколько хорошо это работает, и какие еще альтернативы браузерному JS существуют.

Представляем Brython

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

Создаем игру Змейка с помощью Brython

Пишем змейку
Пишем змейку

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

Для того чтобы попробовать Brython в действии, я решил создать классическую игру «Змейка».
Поскольку я не специалист по HTML Canvas и не разработчик игр, я решил использовать эту реализацию JavaScript в качестве отправной точки. Я уже создавал свою собственную игру-змейку на холсте, но эта намного аккуратнее и компактнее.
О да, и этот парень создал ее менее чем за 5 минут. Спасибо Крису Делеону, это очень впечатляет.
Итак, в дополнение к реализации Криса я добавил функцию подсчета очков и результатов, немного улучшил интерфейс, добавил кнопку паузы и кнопку инструкций. Затем я портировал его на Brython.
На самом деле, я также изменил его код так, чтобы он работал в строгом режиме JS, поскольку он использовал такие вещи, как неявные глобалы, которые, как мне кажется, не являются репрезентативными для большинства JavaScript (не критикую его — он занимался скоростным кодированием), и я хотел получить хорошее сравнение кода 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/[email protected]/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/[email protected]/brython.min.js">

Запуск Brython

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

<body onload=”brython()”>

Он будет искать теги сценариев типа text/python и оценивать код внутри них.

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

По умолчанию JavaScript предоставляет вам доступ к таким объектам, как документ и окно, которые необходимы в любом проекте JS. Таким образом, Brython должен иметь способ включить и их.
Чтобы решить эту проблему, создатели Brython могли бы просто позволить разработчикам по умолчанию сразу обращаться к document и window в коде Python, но это заставило бы отладчики Python постоянно кричать о undefined variable и ухудшило бы производительность.
Таким образом, чтобы использовать эти Web API, мы должны импортировать их так же, как мы это делаем с любым модулем Python:

from browser import document, html, window

На самом деле, вам не нужно выполнять pip install . В конце концов, мы вставляем это в наш HTML! Просто включите операторы импорта, и все в порядке — Brython будет знать, что делать.
Чтобы проверить, насколько хорошо это работает, я попробовал несколько различных методов из Web API, таких как alert , setInterval , addEventListener и т.д. Все сработало, как и ожидалось.

Объекты и методы нативного JavaScript

В игре Snake, после того как змея съест яблоко, нам нужно сгенерировать новое яблоко в случайном месте.
Однако, когда я начинал работать с Brython, я не знал, что могу использовать модуль случайных чисел random  Python (вы можете!). Как же мне сгенерировать случайное число (без создания собственной библиотеки)?
Оказывается, на самом деле мы можем получить доступ к большему количеству JavaScript, чем я думал. Посмотрите на это:

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

С модулем javascript, если есть объект, к которому я могу обратиться с помощью JS, я могу обратиться к нему с помощью Brython.
Например, если я импортирую библиотеку JavaScript (jQuery, Bootstrap) и хочу использовать ее методы — я могу сделать это с помощью from javascript import <library>. И, естественно, таким же образом я могу использовать встроенные объекты JS, такие как Date или String .

Концепции, специфичные для конкретного языка

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

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

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

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

Отладка
Честно говоря, я думал, что отладка Brython будет ужасной.
Однако на самом деле все не так уж плохо.
Конечно, то, что я создал, это небольшой проект, не очень сложный, но ошибки, выдаваемые Brython, были в основном точными и достаточно описательными.
Это верно, по крайней мере, в отношении проблем с синтаксисом. Импорт модулей Python — это совсем другое дело.
Производительность

JavaScript Snake
JavaScript Snake
Brython Snake
Brython Snake

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

Заключительные размышления о Brython

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

Плюсы

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

Минусы

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

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

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

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

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

Однако существуют и другие альтернативы.

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

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

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

Однако он все еще находится в зачаточном состоянии (~3 года), поэтому, вероятно, пройдет некоторое время, прежде чем мы увидим, как JavaScript будет заменен другими языками на регулярной основе.
А пока этого не происходит, вам придется прибегнуть к таким инструментам, как Brython, если вы действительно не можете работать с JavaScript.
Но, честно говоря, это неплохое начало!