郭學聰 Hsueh-Tsung Kuo
Sat, 2 Oct 2021
Someone (who?) said:
a game programmer should be able to draw cute anime character(?)
-- defines a factorial function function fact (n) if n == 0 then return 1 else return n * fact(n-1) end end print("enter a number:") a = io.read("*number") -- read a number print(fact(a))
Major features from official introduction
* separate Lua runtime states through a LuaRuntime class
* Python coroutine wrapper for Lua coroutines
* iteration support for Python objects in Lua and Lua objects in Python
* proper encoding and decoding of strings (configurable per runtime, UTF-8 by default)
* frees the GIL and supports threading in separate runtimes when calling into Lua
* tested with Python 2.7/3.5 and later
* written for LuaJIT2 (tested with LuaJIT 2.0.2), but also works with the normal Lua interpreter (5.1 and later)
* easy to hack on and extend as it is written in Cython, not C
test code
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # from https://alchemypy.com/2020/05/13/speed-your-python-with-lua/ import sys import time from lupa import LuaRuntime def sum_all_to_range_pure_python(end_range_number): start_time = time.time() the_sum = 0 for number in range(1, end_range_number+1): the_sum += number stop_time = time.time() return (end_range_number, stop_time - start_time, the_sum) def sum_all_to_range_with_lua_python(end_range_number): start_time = time.time() lua_code = """ function(n) sum = 0 for i=1,n,1 do sum = sum + i end return sum end """ lua_func = LuaRuntime(encoding=None).eval(lua_code) the_sum = lua_func(end_range_number) stop_time = time.time() return(end_range_number, stop_time - start_time, the_sum) def main(argv=sys.argv[:]): end_range_number = 200000000 python_result = sum_all_to_range_pure_python(end_range_number) lupa_result = sum_all_to_range_with_lua_python(end_range_number) print('python - range: %d, time: %g sec, sum: %d' % python_result) print('lupa - range: %d, time: %g sec, sum: %d' % lupa_result) if __name__ == '__main__': sys.exit(main())
pypy3 > lupa > python3
python3 - range: 200000000, time: 7.86578 sec, sum: 20000000100000000
lupa - range: 200000000, time: 2.94546 sec, sum: 20000000100000000
pypy3 - range: 200000000, time: 0.270106 sec, sum: 20000000100000000
Python & Lua function
>>> import lupa >>> from lupa import LuaRuntime >>> lua = LuaRuntime(unpack_returned_tuples=True) >>> lua.eval('1+1') 2 >>> lua_func = lua.eval('function(f, n) return f(n) end') >>> def py_add1(n): return n+1 >>> lua_func(py_add1, 2) 3 >>> lua.eval('python.eval(" 2 ** 2 ")') == 4 True >>> lua.eval('python.builtins.str(4)') == '4' True
Python dict part 1
>>> lua_func = lua.eval('function(obj) return obj["get"] end') >>> d = {'get' : 'value'} >>> value = lua_func(d) >>> value == d['get'] == 'value' True
Python dict part 2
Python dict part 2
>>> value = lua_func( lupa.as_itemgetter(d) ) >>> value == d['get'] == 'value' True >>> dict_get = lua_func( lupa.as_attrgetter(d) ) >>> dict_get == d.get True >>> dict_get('get') == d.get('get') == 'value' True >>> lua_func = lua.eval( ... 'function(obj) return python.as_attrgetter(obj)["get"] end') >>> dict_get = lua_func(d) >>> dict_get('get') == d.get('get') == 'value' True
Lua table part 1
>>> table = lua.eval('{10,20,30,40}') >>> table[1] 10 >>> table[4] 40 >>> list(table) [1, 2, 3, 4] >>> list(table.values()) [10, 20, 30, 40] >>> len(table) 4
Lua table part 2
>>> mapping = lua.eval('{ [1] = -1 }') >>> list(mapping) [1] >>> mapping = lua.eval('{ [20] = -20; [3] = -3 }') >>> mapping[20] -20 >>> mapping[3] -3 >>> sorted(mapping.values()) [-20, -3] >>> sorted(mapping.items()) [(3, -3), (20, -20)] >>> mapping[-3] = 3 # -3 used as key, not index! >>> mapping[-3] 3 >>> sorted(mapping) [-3, 3, 20] >>> sorted(mapping.items()) [(-3, 3), (3, -3), (20, -20)]
>>> lua.execute(''' ... for k, v in pairs(python) do ... print(k) ... end ... ''') eval iter iterex enumerate none as_attrgetter as_itemgetter as_function builtins
>>> lua.execute(''' ... for i in python.iter(python.builtins.dir(python.builtins)) do ... print(i) ... end ... ''') ArithmeticError AssertionError AttributeError BaseException . . . map max memoryview min next object oct open ord pow print property quit range repr reversed round set setattr slice sorted staticmethod str sum super tuple type vars zip
Different behavior
>>> tbl = lua.eval('{a = 1, b = 2, c = 3}') >>> len(tbl) 0 >>> len(list(tbl.items())) 3 >>> tbl <Lua table at 0x1db1570> >>> tbl.items() LuaIter(<Lua table at 0x1db3570>)
LUA_ISOLATED_RUNTIME_SCRIPT = ''' require = nil package = nil os = nil io = nil python = nil ''' lua.execute(LUA_ISOLATED_RUNTIME_SCRIPT)
Execute it for every new "LuaRuntime"
import luadata def convert_table_to_dict(table): dict_data = {} for (k, v) in table.items(): if lupa.lua_type(v) == 'table': vd = convert_table_to_dict(v) else: vd = v dict_data[k] = vd return dict_data dict_data = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5, 'f': 6}} table = lua.eval(luadata.serialize(dict_data)) # convert dict to table result_table = lua_func(table) result_dict_data = convert_table_to_dict(result_table)
LUA_UTIL_FUNC_SCRIPT = ''' function get_table_size(tbl) local count = 0 for _ in pairs(tbl) do count = count + 1 end return count end function clear_table(tbl) for k, v in pairs(tbl) do tbl[k] = nil end end function get_table(tbl, k, default) local v = tbl[k] if v == nil then v = default end return v end function set_default_to_table(tbl, k, default) if tbl[k] == nil then tbl[k] = default end return tbl[k] end ''' lua.execute(LUA_UTIL_FUNC_SCRIPT)
import lupa, luadata from lupa import LuaRuntime lua = LuaRuntime(unpack_returned_tuples=True) def convert_table_to_dict(table): dict_data = {} for (k, v) in table.items(): if lupa.lua_type(v) == 'table': vd = convert_table_to_dict(v) else: vd = v dict_data[k] = vd return dict_data def convert_dict_to_table(dict_data): return lua.eval(luadata.serialize(dict_data)) LUA_ISOLATED_RUNTIME_SCRIPT = ''' require = nil package = nil os = nil io = nil python = nil ''' lua.execute(LUA_ISOLATED_RUNTIME_SCRIPT) LUA_UTIL_FUNC_SCRIPT = ''' function get_table_size(tbl) local count = 0 for _ in pairs(tbl) do count = count + 1 end return count end function clear_table(tbl) for k, v in pairs(tbl) do tbl[k] = nil end end function get_table(tbl, k, default) local v = tbl[k] if v == nil then v = default end return v end function set_default_to_table(tbl, k, default) if tbl[k] == nil then tbl[k] = default end return tbl[k] end ''' lua.execute(LUA_UTIL_FUNC_SCRIPT) # test dict_data = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5, 'f': 6}} tbl = lua.eval('{a = 1, b = 2, c = {d = 4, e = 5, f = 6}}') lua_func = lua.eval(''' function(tbl) print(tbl) for k, v in pairs(tbl) do print(string.format("%s %s", k, v)) end return tbl end ''')
See demo
成為 Python 與 Lua 的橋梁
郭學聰 Hsueh-Tsung Kuo2021_10_02