子类ctypes.c_int创建一个在ctypes.Structure._fields_中使用的枚举

时间:2017-09-19 21:47:44

标签: python ctypes enumeration

这就是我第一次来自的地方:

class DismRestartType(DismEnum):
    DismRestartNo = 0, 'No Restart'
    DismRestartPossible = 1, 'Restart Possible'
    DismRestartRequired = 2, 'Restart Required'

并使用它:

class DismFeatureInfo(DismStructure):
    _pack_ = 4
    _fields_ = [
        ("FeatureName", c_wchar_p),
        ("RestartRequired", DismRestartType)
    ]

class DismEnum(Enum):

    def __new__(cls, value, description):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.description = description
        return obj

请注意说明的附加参数。计划是稍后显示描述而不是价值,所以我不必为每个结构本身做好准备 问题:我收到错误,因为结构需要c类型并获得枚举 我做了一些研究并发现了这个,即: Using an IntEnum as the type in a ctypes.Structure._fields_

所以我试过了:

class CEnumeration(c_int):

    def __new__(cls, value, description):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.description = description
        print("Will never be executed")
        return obj

    def __repr__(self):
        return self.description

不幸的是,我得到*** AttributeError: 'DismRestartType' object has no attribute 'description'方法也永远不会被执行。有人可以解释一下为什么它没有被执行并帮助我达到目标吗?

EDIT1:
我不明白!为什么__new__未在TestEnum中执行,但在从Enum继承时执行?元类 new 会被执行。

class PointlessMetaClass(type(c_int)):

    def __new__(meta, classname, bases, classDict):
        cls = type(c_int).__new__(meta, classname, bases, classDict)
        pdb.set_trace()  
        return cls

class TestEnum(metaclass=PointlessMetaClass):
    _type_ = "i"

    def __new__(cls, value):
        print("Why not executed")
        pdb.set_trace()
        return cls


class DismRestartType(TestEnum):
    DismRestartNo = 0, 'No Restart'
    DismRestartPossible = 1, 'Restart Possible'
    DismRestartRequired = 2, 'Restart Required'

解决方案: 我花了很长时间,但现在我明白了:

from ctypes import c_int
from types import DynamicClassAttribute


class _EnumDict(dict):
    """Track enum member order and ensure member names are not reused.

    EnumMeta will use the names found in self._member_names as the
    enumeration member names.

    """
    def __init__(self):
        super().__init__()
        self._member_names = []

    def __setitem__(self, key, value):
        if not isinstance(value, DynamicClassAttribute) and not key.startswith("_"):
            self._member_names.append(key)
        super().__setitem__(key, value)


class EnumerationType(type(c_int)):
    """Metaclass for Enum."""
    @classmethod
    def __prepare__(metacls, cls, bases):
        edict = _EnumDict()
        return edict

    def __new__(metacls, classname, bases, classdict):
        # save enum items into separate mapping so they don't get baked into
        # the new class
        enum_members = {k: classdict[k] for k in classdict._member_names}
        for name in classdict._member_names:
            del classdict[name]

        # returns an instance of the new class, i.e. an instance of my enum
        enum_class = super().__new__(metacls, classname, bases, classdict)
        # Reverse value->name map for hashable values.
        enum_class._value2member_map_ = {}

        for member_name in classdict._member_names:
            value = enum_members[member_name][0]
            enum_member = c_int.__new__(enum_class)
            enum_member.value = value # overwrites the value attr of c_int class
            enum_member._name_ = member_name
            enum_member._description_ = enum_members[member_name][1]
            enum_member.__objclass__ = enum_class
            # i.e DismRestartType.DismRestartNo will return an object instead of the value
            setattr(enum_class, member_name, enum_member)
            # i.e. {'0': <class DismRestartType:DismRestartNo: 0>}
            enum_class._value2member_map_[value] = enum_member

        return enum_class

    def __repr__(self):
        return "<Enumeration %s>" % self.__name__

class CEnumeration(c_int, metaclass=EnumerationType):
    """Generic enumeration.

    Derive from this class to define new enumerations.

    """
    def __new__(cls, value):
        # all enum instances are actually created during class construction
        # without calling this method; this method is called by the metaclass'
        # __call__ (i.e. Color(3) ), and by pickle
        if type(value) is cls:
            # For lookups like Color(Color.RED)
            return value
        # by-value search for a matching enum member
        # see if it's in the reverse mapping (for hashable values)
        try:
            if value in cls._value2member_map_:
                return cls._value2member_map_[value]
        except TypeError:
            pass
        return cls._missing_(value)

    @classmethod
    def _missing_(cls, value):
        raise ValueError("%r is not a valid %s" % (value, cls.__name__))

    # return only description
    def __repr__(self):
        return "<%s.%s: %r>" % (
                self.__class__.__name__, self.name, self.value)

    def __str__(self):
        return "%s.%s" % (self.__class__.__name__, self.name)


    # DynamicClassAttribute is used to provide access to the `name` and
    # `value` properties of enum members while keeping some measure of
    # protection from modification, while still allowing for an enumeration
    # to have members named `name` and `value`.  This works because enumeration
    # members are not set directly on the enum class -- __getattr__ is
    # used to look them up.

    @DynamicClassAttribute
    def name(self):
        """The name of the Enum member."""
        try:
            # get name on instance
            return self._name_
        except AttributeError:
            # get name on class
            return self._value2member_map_[self.value]._name_

    @DynamicClassAttribute
    def description(self):
        """The description of the Enum member."""
        try:
            # get description on instance
            return self._description_
        except AttributeError:
            # get description on class
            return self._value2member_map_[self.value]._description_

0 个答案:

没有答案