標籤:htm amp app targe 函數 是你 lvm header call
15.12 將函數指標轉換為可調用對象?問題?
你已經獲得了一個被編譯函數的記憶體位址,想將它轉換成一個Python可調用對象,
這樣的話你就可以將它作為一個擴充函數使用了。
解決方案?
ctypes 模組可被用來建立封裝任意記憶體位址的Python可調用對象。
下面的例子示範了怎樣擷取C函數的原始、底層地址,以及如何將其轉換為一個可調用對象:
>>> import ctypes>>> lib = ctypes.cdll.LoadLibrary(None)>>> # Get the address of sin() from the C math library>>> addr = ctypes.cast(lib.sin, ctypes.c_void_p).value>>> addr140735505915760>>> # Turn the address into a callable function>>> functype = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)>>> func = functype(addr)>>> func<CFunctionType object at 0x1006816d0>>>> # Call the resulting function>>> func(2)0.9092974268256817>>> func(0)0.0>>>
討論?
要構建一個可調用對象,你首先需要建立一個 CFUNCTYPE 執行個體。
CFUNCTYPE() 的第一個參數是傳回型別。
接下來的參數是參數類型。一旦你定義了函數類型,你就能將它封裝在一個整型記憶體位址上來建立一個可調用對象了。
產生的對象被當做普通的可通過 ctypes 訪問的函數來使用。
本節看上去可能有點神秘,偏底層一點。
但是,但是它被廣泛使用於各種進階代碼產生技術比如即時編譯,在LLVM函數庫中可以看到。
例如,下面是一個使用 llvmpy 擴充的簡單例子,用來構建一個小的聚集合函式,擷取它的函數指標,
並將其轉換為一個Python可調用對象。
>>> from llvm.core import Module, Function, Type, Builder>>> mod = Module.new(‘example‘)>>> f = Function.new(mod,Type.function(Type.double(), [Type.double(), Type.double()], False), ‘foo‘)>>> block = f.append_basic_block(‘entry‘)>>> builder = Builder.new(block)>>> x2 = builder.fmul(f.args[0],f.args[0])>>> y2 = builder.fmul(f.args[1],f.args[1])>>> r = builder.fadd(x2,y2)>>> builder.ret(r)<llvm.core.Instruction object at 0x10078e990>>>> from llvm.ee import ExecutionEngine>>> engine = ExecutionEngine.new(mod)>>> ptr = engine.get_pointer_to_function(f)>>> ptr4325863440>>> foo = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double, ctypes.c_double)(ptr)>>> # Call the resulting function>>> foo(2,3)13.0>>> foo(4,5)41.0>>> foo(1,2)5.0>>>
並不是說在這個層面犯了任何錯誤就會導致Python解譯器掛掉。
要記得的是你是在直接跟機器層級的記憶體位址和本地機器碼打交道,而不是Python函數。
艾伯特(http://www.aibbt.com/)國內第一家人工智慧門戶
Python Cookbook(第3版)中文版:15.12 將函數指標轉換為可調用對象