from meta.decompiler import decompile_funcfrom meta.asttools import str_astprint str_ast(decompile_func(add2))FunctionDef(args=arguments(args=[Name(ctx=Param(),id='a'),Name(ctx=Param(),id='b')],defaults=[],kwarg=None,vararg=None),body=[Return(value=https://www.isolves.com/it/cxkf/yy/Python/2020-09-24/BinOp(left=Name(ctx=Load(),id='a'),op=Add(),right=Name(ctx=Load(),id='b')))],decorator_list=[],name='add2')而python_source可以将ast语法树转换为Python源代码:
from meta.asttools import python_sourcepython_source(decompile_func(add2))def add2(a, b):return (a + b)decompile_pyc将上述二者结合起来,它能将Python编译之后的pyc或者pyo文件反编译成源代码 。下面我们先写一个tmp.py文件,然后通过py_compile将其编译成tmp.pyc 。
with open("tmp.py", "w") as f:f.write("""def square_sum(n):s = 0for i in range(n):s += i**2return s""")import py_compilepy_compile.compile("tmp.py")下面调用decompile_pyc将tmp.pyc显示为源代码:
with open("tmp.pyc", "rb") as f:decompile_pyc(f)def square_sum(n):s = 0for i in range(n):s += (i ** 2)return sllvmpy模块LLVM是一个动态编译器,llvmpy则可以通过Python调用LLVM动态地创建机器码 。直接通过llvmpy创建机器码是比较繁琐的,例如下面的程序创建一个计算两个整数之和的函数,并调用它计算结果 。
from llvm.core import Module, Type, Builderfrom llvm.ee import ExecutionEngine, GenericValue# Create a new module with a function implementing this:## int add(int a, int b) {#return a + b;# }#my_module = Module.new('my_module')ty_int = Type.int()ty_func = Type.function(ty_int, [ty_int, ty_int])f_add = my_module.add_function(ty_func, "add")f_add.args[0].name = "a"f_add.args[1].name = "b"bb = f_add.Append_basic_block("entry")# IRBuilder for our basic blockbuilder = Builder.new(bb)tmp = builder.add(f_add.args[0], f_add.args[1], "tmp")builder.ret(tmp)# Create an execution engine object. This will create a JIT compiler# on platforms that support it, or an interpreter otherwiseee = ExecutionEngine.new(my_module)# Each argument needs to be passed as a GenericValue object, which is a kind# of variantarg1 = GenericValue.int(ty_int, 100)arg2 = GenericValue.int(ty_int, 42)# Now let's compile and run!retval = ee.run_function(f_add, [arg1, arg2])# The return value is also GenericValue. Let's print it.print "returned", retval.as_int()returned 142f_add就是一个动态生成的机器码函数,我们可以把它想象成C语言编译之后的函数 。在上面的程序中,我们通过ee.run_function调用此函数,而实际上我们还可以获得它的地址,然后通过Python的ctypes模块调用它 。
首先通过ee.get_pointer_to_function获得f_add函数的地址:
addr = ee.get_pointer_to_function(f_add)addr2975997968L然后通过ctypes.PYFUNCTYPE创建一个函数类型:
import ctypesf_type = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)最后通过f_type将函数的地址转换为可调用的Python函数,并调用它:
f = f_type(addr)f(100, 42)142numba所完成的工作就是:
解析Python函数的ast语法树并加以改造,添加类型信息;
将带类型信息的ast语法树通过llvmpy动态地转换为机器码函数,然后再通过和ctypes类似的技术为机器码函数创建包装函数供Python调用
【一行代码让你的python运行速度提高100倍】
推荐阅读
- RFM模型升级,使用Excel完成全自动分析
- 善用沙盒虚拟机,测试有风险的程序让你无视木马病毒
- 私下转让宅基地有效吗?
- 红豆薏米茶让月经推迟,红豆薏米茶喝多久见效
- 6行代码实现下载一首网易云歌曲
- 安徽|安徽老人乞讨,要钱的态度让人直呼嚣张,好心人:不止一次了
- 雀斑怎么去除 8款食疗方让你提前“下斑”
- 25款食谱让糖尿病不可怕 糖尿病食谱
- 便秘吃什么好 四款食谱让你远离便秘
- 糖尿病人食谱 四款菜肴让你饱口福
