以类为键的 Python dict 有 KeyErrors

Bea*_*ear 2 python ctypes

我正在编写一个程序来使用 ctypes 处理串行流量。同时,我正在与一个数据库库交互,该库使用不同但相似的类将字段应用于数据缓冲区。

我想编写一个函数,它可以采用任意 ctypes 结构,迭代其字段,并调用数据库库。为此,我制作了一张 {ctypes class : database class} 的地图,并且得到了奇怪的 KeyErrors。然而,事实证明数据库库与它无关,您可以使用 {ctypes class : string} 的 dict 看到相同的行为,如下面的最小示例所示:

from ctypes import *

db_map = {
    c_char : "DByte",
    c_byte : "DByte",
    c_ubyte : "DByte",
    c_ushort : "DUShort",
    c_uint16 : "DUShort",
}

class command_buff(BigEndianStructure):
    _pack_   = 1
    _fields_ = [("frame_header",    c_char   ),
                ("command_id",      c_uint8  ), 
                ("param_value",     c_uint8  ),
                ("crc16",           c_uint16 ),
                ("frame_footer",    c_char   )]

def display(buff, database_name):
    """Applies my structure to the Dbuffer named database_name."""
    global db_map
    for key in db_map:
        print(f"{key} {id(key)}")
    print()
    for field_name, c_typ, *rest in buff._fields_:
        stol_typ = db_map.get(c_typ, None)
        if stol_typ is None:
            print(f"  ERROR Can't find type {c_typ} for name {field_name}")
            print(f"  ERROR ({field_name}, {id(c_typ)}, {rest})")
        else:
            print(f"{database_name}.{field_name}")

cb = command_buff
display(cb, "Foo")
Run Code Online (Sandbox Code Playgroud)

在线试试吧!

其中产生:

<class 'ctypes.c_char'> 2337600989576
<class 'ctypes.c_byte'> 2337600987688
<class 'ctypes.c_ubyte'> 2337600959368
<class 'ctypes.c_ushort'> 2337600969752

Foo.frame_header
Foo.command_id
Foo.param_value
  ERROR Can't find type <class 'ctypes.c_ushort'> for name crc16
  ERROR (crc16, 2337600963144, [])
Foo.frame_footer
Run Code Online (Sandbox Code Playgroud)

如您所见,class 'ctypes.c_ushort'dict 中的 IDclass 'ctypes.c_ushort'_fields_成员中的ID 不同,这大概是它认为它不在 dict 中的原因。但我不明白怎么会是这样,考虑到它们都来自完全相同的导入语句。

我已经看过了问题,比如这一个,但多数似乎处理多个实例具有不同ID的一类。在这里,即使在这么短的程序中,类本身似乎有多个 ID。

为什么会发生这种情况的幕后解释是什么?

按类键入 dict 的正确方法是什么,或者(如果这是一件愚蠢的事情)以实现映射类 -> 类的目标?

AKX*_*AKX 6

这是 ctypes 的 BigEndianStructure 如何工作的内部怪癖。

深入研究ctypes/_endian.py,我发现 ctypes 类型具有用于其自身(__ctype_be____ctype_le__)的显式端序版本的内部类型。

字节序结构类型的元类_fields_在其他字节序机器上交换类型的字节序。

如果您有 BigEndianStructure,则您的映射还需要使用这些 BigEndian ( _be) 类型:

db_map = {
    c_char.__ctype_be__: "DByte",
    c_byte.__ctype_be__: "DByte",
    c_ubyte.__ctype_be__: "DByte",
    c_ushort.__ctype_be__: "DUShort",
    c_uint16.__ctype_be__: "DUShort",
}
Run Code Online (Sandbox Code Playgroud)

使用较少下划线的另一个选项可能是仅使用__name__类型的 并具有字符串类型的 dict:

db_map = {
    "c_char": "DByte",
    "c_byte": "DByte",
    "c_ubyte": "DByte",
    "c_ushort_be": "DUShort",
    "c_ushort_le": "DUShort",
    "c_uint16": "DUShort",
}

# ...

db_map.get(c_typ.__name__)
Run Code Online (Sandbox Code Playgroud)