2010-12-04 35 views
22

में डेटा प्राप्त करना मैं गणना के एक श्रृंखला को चलाने के लिए एक पायथन (ctypes) लपेटा सी लाइब्रेरी का उपयोग कर रहा हूं। चलने के विभिन्न चरणों में, मैं पाइथन में डेटा प्राप्त करना चाहता हूं, और विशेष रूप से numpy सरणी प्राप्त करना चाहता हूं।ctypes सरणी से numpy

रैपिंग मैं उपयोग कर रहा हूँ सरणी डेटा के लिए वापसी (मेरे लिए विशेष रुचि का है जो) दो अलग अलग प्रकार है:

  • ctypes सरणी: जब मैं type(x) (कर जहां x ctypes है सरणी, मैं बदले में एक <class 'module_name.wrapper_class_name.c_double_Array_12000'> मिल मुझे पता है कि इस डेटा प्रलेखन से आंतरिक डेटा की एक प्रतिलिपि है और मैं एक numpy सरणी में यह आसानी से प्राप्त करने में सक्षम हूँ:।

    >>> np.ctypeslib.as_array(x) 
    

यह डेटा के 1 डी numpy सरणी देता है।

  • ctype डेटा सूचक: पुस्तकालय के दस्तावेज़ से इस मामले में, मैं समझता हूँ कि मैं संग्रहीत और पुस्तकालय के लिए सीधे इस्तेमाल किया डेटा के लिए सूचक हो रही है। मट्ठा मैं type(y) (जहां y सूचक है) मुझे <class 'module_name.wrapper_class_name.LP_c_double'> मिलता है। इस मामले के साथ मैं अब भी y[0][2] की तरह डेटा के माध्यम से सूचकांक करने में सक्षम हूँ, लेकिन मैं केवल एक सुपर अजीब के माध्यम से यह numpy में प्राप्त करने में सक्षम था:

    >>> np.frombuffer(np.core.multiarray.int_asbuffer(
        ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize)) 
    

मैं एक पुराने numpy मेलिंग सूची thread from Travis Oliphant में यह पाया , लेकिन numpy दस्तावेज में नहीं। इसके बाद के संस्करण के रूप में मैं निम्नलिखित मिलता है इस दृष्टिकोण के बजाय मैं कोशिश:

>>> np.ctypeslib.as_array(y) 
... 
... BUNCH OF STACK INFORMATION 
... 
AttributeError: 'LP_c_double' object has no attribute '__array_interface__' 

इस np.frombuffer सबसे अच्छा या एक ही तरीका है यह करने के लिए दृष्टिकोण है? मैं अन्य सुझावों के लिए खुला हूं लेकिन अभी भी numpy का उपयोग करना चाहूंगा क्योंकि मेरे पास अन्य पोस्ट-प्रोसेसिंग कोड है जो numpy कार्यक्षमता पर निर्भर करता है जिसे मैं इस डेटा के साथ उपयोग करना चाहता हूं।

+0

हैं आप नियंत्रण ओ है सी सी lib? क्या आप लाइब्रेरी का एपीआई बदल सकते हैं? –

+0

हां - मेरे पास स्रोत है। मुझे यकीन नहीं है कि किस तरह से जाना है, क्योंकि सूचक दृष्टिकोण पाइथन को डेटा पर सीधे कार्य करने की अनुमति देता है जो मुझे लगता है कि कुछ मामलों में मुझे लगता है कि एक फायदा हो सकता है। हालांकि मेरे मामले में, हाँ, यह सब कुछ होगा कि सब कुछ 'ctype' सरणी के रूप में बाहर आ जाए। कोई सिफारिशें? – dtlussier

+1

मैं लाइब्रेरी में आपके द्वारा पाइथन में आवंटित एक लाइब्रेरी (NumPy-) सरणी का उपयोग करने और लाइब्रेरी पर जाने का सुझाव दूंगा। इस तरह, आप एक ही स्मृति पर कार्य कर सकते हैं, लेकिन आपको किसी भी अजीब रूपांतरण करने की परेशानी नहीं है। आपके पास पहले से एक NumPy सरणी है, और इसे लाइब्रेरी में पास करना ['numpy.ctypeslib.ndpointer'] (http://docs.scipy.org/doc/numpy/reference/routines.ctypeslib.html का उपयोग करके अच्छी तरह से समर्थित है) # numpy.ctypeslib.ndpointer) आपके फ़ंक्शन के ctypes wrapper के लिए तर्क प्रकार के रूप में। (यदि यह स्पष्ट नहीं है, तो बस पूछें ...) –

उत्तर

23

एक प्रकार के पॉइंटर ऑब्जेक्ट से न्यूमपी सरणी बनाना एक समस्याग्रस्त ऑपरेशन है। यह स्पष्ट नहीं है कि वास्तव में पॉइंटर इंगित करने वाली स्मृति का मालिक है। इसे फिर से कब मुक्त किया जाएगा? यह कब तक वैध है? जब भी संभव हो मैं इस तरह के निर्माण से बचने की कोशिश करता हूं। पाइथन कोड में सरणी बनाने के लिए यह बहुत आसान और सुरक्षित है और उन्हें पाइथन-अनजान सी फ़ंक्शन द्वारा आवंटित स्मृति का उपयोग करने के बजाय सी फ़ंक्शन में पास करना है। उत्तरार्द्ध करके, आप स्मृति प्रबंधन की देखभाल करने वाली उच्च स्तरीय भाषा रखने के फायदों के कुछ हद तक अस्वीकार करते हैं।

यदि आप वास्तव में सुनिश्चित हैं कि कोई मेमोरी का ख्याल रखता है, तो आप पाइथन "बफर प्रोटोकॉल" को उजागर करने वाली ऑब्जेक्ट बना सकते हैं और फिर इस बफर ऑब्जेक्ट का उपयोग करके एक NumPy सरणी बना सकते हैं।

buffer = numpy.core.multiarray.int_asbuffer(
    ctypes.addressof(y.contents), 8*array_length) 

(ध्यान दें कि मैं np.dtype(float).itemsize के लिए 8 प्रतिस्थापित यह हमेशा 8 है, किसी भी मंच पर:। आप गैर-दस्तावेजी int_asbuffer() समारोह के माध्यम से अपनी पोस्ट में बफर वस्तु बनाने में से एक रास्ता दिया,।) बफर वस्तु बनाने के लिए एक अलग तरह ctypes के माध्यम से अजगर सी एपीआई से PyBuffer_FromMemory() समारोह कॉल करने के लिए होगा:

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory 
buffer_from_memory.restype = ctypes.py_object 
buffer = buffer_from_memory(y, 8*array_length) 

इन दोनों तरीकों के लिए, आप

a = numpy.frombuffer(buffer, float) 
द्वारा buffer से एक NumPy सरणी बना सकते हैं

(मैं वास्तव में समझ में नहीं तुम क्यों frombuffer करने के लिए एक दूसरा पैरामीटर के बजाय .astype() का उपयोग करें; इसके अलावा, मुझे आश्चर्य है कि क्यों तुम np.int उपयोग करते हैं, जबकि आप पहले कि सरणी double रों होता है कहा।)

मुझे डर है कि इससे इससे ज्यादा आसान नहीं होगा, लेकिन यह बुरा नहीं है, आपको नहीं लगता? आप एक बदसूरत समारोह में सभी बदसूरत विवरण दफन कर सकते हैं और इसके बारे में चिंता न करें।

+0

यह बढ़िया है - पेशेवरों और विपक्ष के अवलोकन के लिए धन्यवाद। '.astype()' कॉल सिर्फ एक आकस्मिक प्रतिलिपि और पिछली त्रुटि थी। मैंने इसे अब अपने प्रश्न से बाहर खींच लिया है। उस पर लेने के लिए धन्यवाद। – dtlussier

7

एक और संभावना (जिसे पहले उत्तर लिखा गया था, पुस्तकालयों के अधिक हाल के संस्करणों की आवश्यकता हो सकती है - मैंने ctypes 1.1.0 और numpy 1.5.0b2 के साथ कुछ ऐसा परीक्षण किया) पॉइंटर से सरणी में कनवर्ट करना है।

np.ctypeslib.as_array(
    (ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents))) 

यह अभी भी साझा स्वामित्व अर्थ विज्ञान के लिए लगता है, इसलिए आप शायद यकीन है कि आप अंतर्निहित बफर अंत में मुक्त कर कि बनाने की जरूरत है।

+2

या numpy से विशेष समर्थन के बिना: आप 'y' सूचक को एक सूचक में एक सूचक में परिवर्तित कर सकते हैं: 'ap = ctypes.cast (y, ctypes.POINTER (ArrayType))' जहां 'ArrayType = ctypes.c_double * array_length' और उस से numpy सरणी बनाएँ: 'a = np.frombuffer (ap.contents)'। देखें [पॉइंटर से सी सरणी को पायथन सरणी में कैसे परिवर्तित करें] (http://stackoverflow.com/questions/7543675/how-to-convert-pointer-to-c-array-to-python-array) – jfs

+0

मैं कोशिश कर रहा था यह, लेकिन एपी ऑब्जेक्ट में सदस्य, सामग्री नहीं है। –

+0

@TotteKarlsson: लिंक से कोड इस तरह काम करता है (मैंने इसका परीक्षण किया है)। यह शायद आपके कोड में एक बग है (यह विभिन्न पायथन संस्करणों के बीच भी अंतर हो सकता है लेकिन यह कम संभावना है)। यदि आपने इसे ठीक नहीं किया है; [एक न्यूनतम लेकिन पूर्ण कोड उदाहरण बनाएँ] (http: // stackoverflow।com/help/mcve), निर्दिष्ट करें कि आपका ओएस, पायथन संस्करण क्या है और [इसे एक नए SO प्रश्न के रूप में पोस्ट करें] (http://stackoverflow.com/questions/ask) – jfs

5

इन अजगर 3. में मेरे लिए काम किया अजगर 2 और 3 में एक numpy ndarray में एक ctypes सूचक परिवर्तित करने के लिए एक सामान्य समाधान मैं इस काम मिल गया के रूप में (केवल पढ़ने के लिए बफर हो रही के माध्यम से) में से कोई:

def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True): 
    arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize 
    if sys.version_info.major >= 3: 
     buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory 
     buf_from_mem.restype = ctypes.py_object 
     buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int) 
     buffer = buf_from_mem(c_pointer, arr_size, 0x100) 
    else: 
     buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory 
     buf_from_mem.restype = ctypes.py_object 
     buffer = buf_from_mem(c_pointer, arr_size) 
    arr = np.ndarray(tuple(shape[:]), dtype, buffer, order=order) 
    if own_data and not arr.flags.owndata: 
     return arr.copy() 
    else: 
     return arr 
0

आप अजगर में सरणियों बनाने ठीक लगता है, 2 डी सरणी के साथ निम्न उदाहरण python3 में काम करता है:

import numpy as np 
import ctypes 

OutType = (ctypes.c_float * 4) * 6 
out = OutType() 
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname 
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType] 
YourCfunction(input1, input2, out) 
out = np.array(out) # convert it to numpy 

print(out) 

numpy और ctypes संस्करणों 1.11.1 और 1.1.0