[英]python 3.3 dict: how to convert struct PyDictKeysObject to python class?
我正在嘗試修改Brandon Rhodes代碼例程,它檢查CPython字典的內部,以便它適用於CPython 3.3。
我相信我已成功翻譯了這個結構。
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
]
我的問題是訪問Cpython 3.3中實現的python字典的C結構。 我開始使用cpython / Objects / dictobject.c和Include / dictobject.h中提供的C結構。 定義字典涉及三個C結構:PyDictObject,PyDictKeysObject和PyDictKeyEntry。 每個C結構到python的正確轉換如下。 評論表明我需要修復的地方。 感謝@eryksun指導我一路走來!
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)
以下函數提供對python字典底層的PyDictObject的訪問:
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
如果d是具有鍵值對的python字典,則obj是包含鍵值對的PyDictObject實例:
obj = cast(id(d), POINTER(PyDictObject)).contents
PyDictKeysObject的一個實例是:
key_obj = obj.ma_keys.contents
指向存儲在字典的插槽0中的密鑰的指針是:
key_obj.dk_entries[0].me_key
使用這些類的程序以及探測插入到字典中的每個鍵的哈希沖突的例程位於此處 。 我的代碼是由Brandon Rhodes為python 2.x編寫的代碼的修改。 他的代碼就在這里 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.