2014-04-27 6 views
5

मैं ब्रैंडन रोड्स कोड Routines that examine the internals of a CPython dictionary को संशोधित करने की कोशिश कर रहा हूं ताकि यह सीपीथन 3.3 के लिए काम करे।पायथन 3.3 dict: पाइथन कक्षा में स्ट्रक्चर PyDictKeysObject को कैसे परिवर्तित करें?

मेरा मानना ​​है कि मैंने इस संरचना का सफलतापूर्वक अनुवाद किया है।

typedef PyDictKeyEntry *(*dict_lookup_func) 
    (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr); 

struct _dictkeysobject { 
    Py_ssize_t dk_refcnt; 
    Py_ssize_t dk_size; 
    dict_lookup_func dk_lookup; 
    Py_ssize_t dk_usable; 
    PyDictKeyEntry dk_entries[1]; 
}; 

मुझे लगता है कि निम्नलिखित अब अच्छा लग रहा है:

from ctypes import Structure, c_ulong, POINTER, cast, py_object, CFUNCTYPE 

LOOKUPFUNC = CFUNCTYPE(POINTER(PyDictKeyEntry), POINTER(PyDictObject), 
         py_object, c_ulong, POINTER(POINTER(py_object))) 

class PyDictKeysObject(Structure): 
"""A key object""" 
_fields_ = [ 
    ('dk_refcnt', c_ssize_t), 
    ('dk_size', c_ssize_t), 
    ('dk_lookup', LOOKUPFUNC), 
    ('dk_usable', c_ssize_t), 
    ('dk_entries', PyDictKeyEntry * 1), 
] 

PyDictKeysObject._dk_entries = PyDictKeysObject.dk_entries 
PyDictKeysObject.dk_entries = property(lambda s: 
    cast(s._dk_entries, POINTER(PyDictKeyEntry * s.dk_size))[0]) 

कोड की यह पंक्ति अब, काम करता है जहां d == {0: 0, 1: 1, 2: 2, 3: 3}:

obj = cast(id(d), POINTER(PyDictObject)).contents # works!!` 

यहाँ C struct PyDictObject से मेरे अनुवाद है:

class PyDictObject(Structure): # an incomplete type 
    """A dictionary object.""" 

def __len__(self): 
    """Return the number of dictionary entry slots.""" 
    pass 

def slot_of(self, key): 
    """Find and return the slot at which `key` is stored.""" 
    pass 

def slot_map(self): 
    """Return a mapping of keys to their integer slot numbers.""" 
    pass 

PyDictObject._fields_ = [ 
    ('ob_refcnt', c_ssize_t), 
    ('ob_type', c_void_p), 
    ('ma_used', c_ssize_t), 
    ('ma_keys', POINTER(PyDictKeysObject)), 
    ('ma_values', POINTER(py_object)), # points to array of ptrs 
] 
+0

नोट: आप [hg.python.org सीधे] (http://hg.python.org/cpython/file/3.3/Objects/dictobject.c#l72) से लिंक कर सकते हैं। 'Dict_lookup_func' परिभाषित करने के लिए' ctypes.CFUNCTYPE' आज़माएं। – jfs

+0

अपडेट: मैंने अब CFUNCTYPE का उपयोग करके dk_lookup का प्रकार घोषित किया है: – LeslieK

+0

@ जेएफ। सेबेस्टियन: धन्यवाद। मैंने अब CFUNCTYPE का उपयोग कर dk_lookup के प्रकार की घोषणा की है। क्या dk_entries सही दिखते हैं? सी कोड dk_entries [1] का उपयोग करता है। – LeslieK

उत्तर

3

मेरी समस्या Cpython 3.3 में लागू एक पायथन शब्दकोश के अंतर्गत सी संरचना को एक्सेस करना था। मैंने cpython/Objects/dictobject.c में प्रदान किए गए सी structs के साथ शुरू किया और/dictobject.h शामिल करें। शब्दकोश को परिभाषित करने में तीन सी structs शामिल हैं: PyDictObject, PyDictKeysObject, और PyDictKeyEntry। पाइथन में प्रत्येक सी संरचना का सही अनुवाद निम्नानुसार है। टिप्पणियां इंगित करती हैं कि मुझे फिक्सेस बनाने की आवश्यकता है। रास्ते में मुझे मार्गदर्शन करने के लिए @eryksun के लिए धन्यवाद !!

def dictobject(d): 
    """Return the PyDictObject lying behind the Python dict `d`.""" 
    if not isinstance(d, dict): 
     raise TypeError('cannot create a dictobject from %r' % (d,)) 
    return cast(id(d), POINTER(PyDictObject)).contents 

तो घ कुंजी-मान जोड़ों के साथ एक अजगर शब्दकोश है, तो obj PyDictObject उदाहरण है कि कुंजी होता है:

class PyDictKeyEntry(Structure): 
"""An entry in a dictionary.""" 
    _fields_ = [ 
     ('me_hash', c_ulong), 
     ('me_key', py_object), 
     ('me_value', py_object), 
    ] 

class PyDictObject(Structure): 
    """A dictionary object.""" 
    pass 

LOOKUPFUNC = CFUNCTYPE(POINTER(PyDictKeyEntry), POINTER(PyDictObject), py_object, c_ulong, POINTER(POINTER(py_object))) 

class PyDictKeysObject(Structure): 
"""An object of key entries.""" 
    _fields_ = [ 
     ('dk_refcnt', c_ssize_t), 
     ('dk_size', c_ssize_t), 
     ('dk_lookup', LOOKUPFUNC), # a function prototype per docs 
     ('dk_usable', c_ssize_t), 
     ('dk_entries', PyDictKeyEntry * 1), # an array of size 1; size grows as keys are inserted into dictionary; this variable-sized field was the trickiest part to translate into python 
    ] 

PyDictObject._fields_ = [ 
    ('ob_refcnt', c_ssize_t), # Py_ssize_t translates to c_ssize_t per ctypes docs 
    ('ob_type', c_void_p),  # could not find this in the docs 
    ('ma_used', c_ssize_t), 
    ('ma_keys', POINTER(PyDictKeysObject)), 
    ('ma_values', POINTER(py_object)), # Py_Object* translates to py_object per ctypes docs 
] 

PyDictKeysObject._dk_entries = PyDictKeysObject.dk_entries 
PyDictKeysObject.dk_entries = property(lambda s: cast(s._dk_entries, POINTER(PyDictKeyEntry * s.dk_size))[0]) # this line is called every time the attribute dk_entries is accessed by a PyDictKeyEntry instance; it returns an array of size dk_size starting at address _dk_entries. (POINTER creates a pointer to the entire array; the pointer is dereferenced (using [0]) to return the entire array); the code then accesses the ith element of the array) 

निम्नलिखित समारोह अजगर शब्दकोश अंतर्निहित PyDictObject तक पहुँच प्रदान करता मान युग्म:

obj = cast(id(d), POINTER(PyDictObject)).contents 

PyDictKeysObject का एक उदाहरण है:

key_obj = obj.ma_keys.contents 

शब्दकोश के स्लॉट 0 में संग्रहीत कुंजी के लिए एक सूचक है:

key_obj.dk_entries[0].me_key 

कार्यक्रम दिनचर्या है कि एक शब्दकोश में डाला प्रत्येक कुंजी के हैश टकराव की जांच के साथ साथ इन कक्षाओं का उपयोग करता है, स्थित है here। मेरा कोड पाइथन 2.x के लिए ब्रैंडन रोड्स द्वारा लिखे गए कोड का एक संशोधन है। उनका कोड here है।

+0

@eryksun मुझे पता था कि मैं "एक सरणी के लिए एक सूचक, पूरी सरणी" द्वारा उलझन में था। जब मैं अपने भ्रम की खोज करने के लिए वापस आया तो मैंने आपकी टिप्पणी देखी और इसकी सराहना की। तो जब हम विशेषता dk_entries तक पहुंचते हैं तो हम पूरी सरणी लौट रहे हैं। संरचना को पूरी सरणी रखने के लिए परिभाषित क्यों किया जाता है? [एली बेंडरस्की टिप्पणियां] (http://eli.thegreenplace.net/2010/01/11/pointers-to-arrays-in-c/): वास्तव में, मैं कल्पना नहीं कर सकता कि कोई एक सरणी के लिए सूचक का उपयोग क्यों करेगा असल ज़िन्दगी में। – LeslieK

+0

एली के उदाहरण पैरामीटर के रूप में 'int (* p) [4] ', या वैकल्पिक रूप से' int p [] [4]' का उपयोग करने के लिए एक सामान्य मामला याद करते हैं। वह केवल '(* पी) [2] = 10' देखता है, लेकिन सी यहां जानता है कि' * पी' 4 'int' मानों की एक सरणी है, इसलिए' पी [1] '' 4 * आकार (int) जोड़ता है आधार पते पर। तो अब हम सहजता से इसे एन एक्स 4 2 डी सरणी के रूप में संभाल सकते हैं, उदा। 'पी [1] [2] = 10'। सी 99 हमें कॉलम की संख्या को तर्क के रूप में पास करने देता है, उदाहरण के लिए 'शून्य परीक्षण (size_t n, size_t m, int p [] [m])'। – eryksun

+0

* "संरचना को पूरी सरणी रखने के लिए परिभाषित क्यों किया गया है?" * मैंने गहराई से इसका अध्ययन नहीं किया है। मुझे लगता है कि यह छोटे dicts के लिए प्रदर्शन में सुधार कर सकते हैं; इसे एक कॉल में एक कॉल में आवंटित किया जाता है जो कैश इलाके में सुधार करता है। लेकिन आपको वास्तव में डिजाइन के साथ किसी और से परिचित होना होगा ... – eryksun

संबंधित मुद्दे