加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python 3.3 dict:如何将struct PyDictKeysObject转换为python类

发布时间:2020-12-16 21:30:34 所属栏目:Python 来源:网络整理
导读:我正在尝试修改Brandon Rhodes代码 Routines that examine the internals of a CPython dictionary,以便它适用于CPython 3.3. 我相信我已成功翻译了这个结构. typedef PyDictKeyEntry *(*dict_lookup_func) (PyDictObject *mp,PyObject *key,Py_hash_t hash,
我正在尝试修改Brandon Rhodes代码 Routines that examine the internals of a CPython dictionary,以便它适用于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),POINTER(POINTER(py_object)))

class PyDictKeysObject(Structure):
"""A key object"""
_fields_ = [
    ('dk_refcnt',c_ssize_t),('dk_size',('dk_lookup',LOOKUPFUNC),('dk_usable',('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',('ob_type',c_void_p),('ma_used',('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',]

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

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

class PyDictKeysObject(Structure):
"""An object of key entries."""
    _fields_ = [
        ('dk_refcnt',# a function prototype per docs 
        ('dk_usable',# 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',# Py_ssize_t translates to c_ssize_t per ctypes docs
    ('ob_type',# could not find this in the docs
    ('ma_used',# 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

使用这些类的程序以及探测插入到字典中的每个键的哈希冲突的例程位于here.我的代码是由Brandon Rhodes为python 2.x编写的代码的修改.他的代码是here.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读