Python + Lua: как подружить два языка в одном проекте
Как объединить Python и Lua в одном проекте, зачем это нужно и какие есть способы интеграции
🤝 Пара «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 — правила, конфиги или «горячие» вычисления?