Python + Lua: как подружить два языка в одном проекте

Как объединить Python и Lua в одном проекте, зачем это нужно и какие есть способы интеграции

РазработкаLuaPython

6 мин

🤝 Пара «Python + Lua» сочетает богатую экосистему Питона и скорость/встраиваемость Lua.

Ниже — 3 рабочих способа интеграции с примерами, подсказками по обмену данными и чеклистами для быстрого старта.

🔍 Зачем объединять Python и Lua

Python

Lua

Море библиотек: веб, AI, данные

Минимальный рантайм, легко встраивается

Простой код, быстрый прототип

Часто быстрее в tight‑циклах (LuaJIT)

API/бэкенд, обработка данных

Скриптуем движки/игры, плагины

🎯 Типовые сценарии

  • Игры и симуляции: сервер/инструменты на Python, гейм‑логика на Lua.

  • Плагины: пользователи пишут расширения на Lua, ядро — на Python.

  • Автоматизация: Python‑оркестрация, Lua‑скрипты под задачи.

🛠 Три способа интеграции

1) Встраиваем Lua в Python через lupa (LuaJIT)

pip install lupa

Python → вызывает Lua

from lupa import LuaRuntime
lua = LuaRuntime(unpack_returned_tuples=True)

# 1) Получаем функцию напрямую
greet = lua.eval('function(name) return "Привет, " .. name .. "!" end')
print(greet("Кодик"))  # Привет, Кодик!

# 2) Работаем с таблицей как с объектом
Vector = lua.eval('''
  function (x, y)
    return { x = x or 0, y = y or 0,
      len = function(self) return math.sqrt(self.x*self.x + self.y*self.y) end
    }
  end
''')
v = Vector(3, 4)
print(v.len(v))  # 5.0

Lua → вызывает Python (обратные вызовы)

from lupa import LuaRuntime
lua = LuaRuntime()
def py_log(msg): print("[PY]", msg)

lua.globals().py_log = py_log
lua.execute('py_log("Привет из Lua!")')  # [PY] Привет из Lua!

# Передаём Python-функцию в Lua-алгоритм
lua.execute('''
  function apply_twice(fn, x) return fn(fn(x)) end
''')
apply_twice = lua.eval('apply_twice')
print(apply_twice(lambda v: v * 2, 5))  # 20

Плюсы: просто, очень быстро (LuaJIT), двусторонние вызовы.

Минусы: зависимость от бинарей, нюансы типов.

2) Запускаем Lua как процесс (subprocess + JSON)

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

Lua‑скрипт (stdin → stdout)

-- file: worker.lua
local json = require("dkjson")   -- или cjson, если доступен
local input = io.read("*a")
local req = json.decode(input)

local result = { sum = req.a + req.b, ok = true }
io.write(json.encode(result))

Python‑обёртка

import json, subprocess

payload = json.dumps({"a": 2, "b": 40})
proc = subprocess.run(
    ["lua", "worker.lua"],
    input=payload, text=True, capture_output=True, check=True
)
res = json.loads(proc.stdout)
print(res["sum"])  # 42

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

3) Расширенно: общий C‑слой / FFI

Подходит, когда есть существующий C/C++‑модуль: Python и Lua линкуются к одному нативному коду. Для старта новичку достаточно знать, что такой путь есть; глубже — позже.

🔄 Обмен данными без боли

Что передаём

Как передаём

Комментарий

Числа, строки, bool

Аргументы функции (lupa) / JSON

Учитывай, что в Lua числа по умолчанию float

Массивы/таблицы

Lua‑таблица ↔ Python‑list/dict

Для сложных структур — сериализация в JSON/MsgPack

Бинарные данные

Base64 / файлы / shared memory

В subprocess‑схеме удобнее файл/pipe

Частые грабли: плавающие типы (int vs float), кодировка строк (UTF‑8!), рекурсия в структурах.

🧯 Ошибки и логирование

Исключения

  • lupa: Lua‑ошибка поднимет Python‑исключение — оборачивай вызовы try/except.

  • subprocess: проверяй returncode и парсь stderr.

Логи

  • Сделай общий префикс логов: [PY] и [LUA].

  • Для subprocess — логируй вход/выход (обрезая большие payload'ы).

💡 Мини‑пример: «правила» на Lua, сервис на Python

-- rules.lua
return {
  discount = function(price)
    if price >= 100 then return price * 0.9 end
    return price
  end
}
# service.py
from lupa import LuaRuntime
lua = LuaRuntime()
rules = lua.eval("dofile")("rules.lua")

def final_price(x): return rules["discount"](x)
print(final_price(150))  # 135.0

💡 Мини‑пример: мини‑RPC через JSON

-- rpc.lua (Lua-процесс)
local json = require "dkjson"
local fn = {
  hello = function(args) return "Hi, " .. (args.name or "anon") end,
  add   = function(a,b) return a+b end
}
local req = json.decode(io.read("*a"))
local ok, out = pcall(function()
  if req.fn == "add" then return fn.add(req.a, req.b)
  else return fn.hello(req) end
end)
io.write(json.encode({ ok = ok, result = out }))
# py_rpc.py
import json, subprocess
def call(fn, **kw):
    req = json.dumps(dict(fn=fn, **kw))
    p = subprocess.run(["lua","rpc.lua"], input=req, text=True,
                       capture_output=True, check=True)
    return json.loads(p.stdout)["result"]

print(call("hello", name="Кодик"))
print(call("add", a=2, b=40))

📚 Хочешь углубиться в тему?

В приложении Кодик ты найдёшь подробные уроки по Puthon и Lua, пошаговые упражнения, разбор ошибок и удобную практику прямо в телефоне или браузере.

А если хочешь быть в курсе новостей, новых фич и полезных материалов — подписывайся на наш Telegram-канал. Там уютно, по делу и с любовью к коду ❤️

💬 Что ты вынесешь в Lua — правила, конфиги или «горячие» вычисления?

Комментарии