【问题标题】:Ordering enum value in python在python中排序枚举值
【发布时间】:2013-08-26 06:58:17
【问题描述】:

我希望能够安排 Enum 的排序。有人建议如何解决这个问题?

以下枚举元类正在使用:

class EnumMeta(type):
    def __new__(typ, name, bases, attrs):
        cls_attrs = {}
        cls_choices = []
        for attr_name, value in attrs.items():
            cls_attrs[attr_name] = attr_name.lower()
            if not attr_name.startswith("__"):
                cls_choices.append((attr_name.lower(), value))

        def choices(cls):
            return cls_choices

        def values(cls, value=None):
            if value is None:
                return {choice[0]: unicode(choice[1]) for choice in cls.choices()}
            elif isinstance(value, list):
                return {choice[0]: unicode(choice[1]) for choice in cls.choices() if choice[0] in value}
            else:
                return unicode(dict(cls.choices()).get(value))

        def keys(cls, nil=False):
            items = [item[0] for item in cls.choices()]
            if nil:
                items.append('')

            return items

        def combined_length(cls):
            return len(",".join(cls.values().keys()))

        def max_length(cls):
            return max(map(len, cls.values().keys()))

        cls_attrs['choices'] = classmethod(choices)
        cls_attrs['values'] = classmethod(values)
        cls_attrs['keys'] = classmethod(keys)
        cls_attrs['combined_length'] = classmethod(combined_length)
        cls_attrs['max_length'] = classmethod(max_length)

        return type(name, bases, cls_attrs)

一个枚举的例子如下:

class SideHemType:
    __ordering__ = ['double', 'single']
    __metaclass__ = EnumMeta

    Single = "Single side hem for opaque fabrics"
    Double = "Double side hem for transparent fabrics"


  class TestEnumOrdering:
        print SideHemType.keys()
        print SideHemType.values() 

通过打印 Enum SideHemType,首先打印 Double,然后打印 Single。但我想先单,然后双。

【问题讨论】:

  • 我在您提供的代码中看不到任何打印内容。我的疯狂猜测是您正在打印dict,它对键的顺序提供任何保证。
  • 我觉得缩进终于对了……
  • dict 是 class.__dict__,不,没有排序
  • 在代码中添加了排序,但它并没有解决我的问题。通过打印键,它的打印首先是单一的,然后是双重的。但就像在示例中一样,它应该是先双后单。
  • 什么?您的评论——“但在示例中,它应该先双后单”与您的帖子相反——“但我希望先单后双”。应该是哪个?

标签: python enums meta


【解决方案1】:

如果您使用的是 Python3.4,则可以使用新的 enum.Enum 类型,它会记住声明枚举成员的顺序。

如果您使用的是较早的 Python,则应使用可用的 enum34from PyPI,它支持 Python 回到 2.4。

enum34 包,如果在 Python3 中使用,也会记住成员声明的顺序。如果在 Python 2 中使用,它支持额外的 _order_ 属性:

from enum import Enum

class SideHemType(Enum):

    _order_ = 'Single Double'  # only needed in Python 2

    Single = "Single side hem for opaque fabrics"
    Double = "Double side hem for transparent fabrics"

    @classmethod
    def combined_length(cls):
        return len(",".join(mbr.name for mbr in cls))

    @classmethod
    def max_length(cls):
        return max(map(len, (mbr.name for mbr in cls)))


print list(SideHemType)  # [SideHemType.Single, SideHemType.Double]

print SideHemType.Double.value  # "Double side hem for transparent fabrics"

【讨论】:

  • 值得注意:即使使用 Python 3.4+,如果您有自己的 自己的列表要排序的枚举成员,则必须指定 key (@987654328 @)。枚举成员不实现__lt__。你会得到一个不错的 TypeError: '<' not supported between instances of 你的 Enum 类型。
  • @Michael-Where'sClayShirky....但是排序顺序是基于值,而不是声明的顺序,对吧?
  • 如果您使用的是 Python 3.4+ Enum,那么您可以在 Enum 定义中定义自己的 __lt__ 运算符来比较名称、值或您想要的任何其他内容。
【解决方案2】:

使用enum 包中的IntEnum 并使用整数值指定您想要的顺序:

class Shape(IntEnum):
    CIRCLE = 1
    SQUARE = 2
Shape.CIRCLE < Shape.SQUARE

打印True

【讨论】:

  • 简单而优雅,注意这在 Python 3.4 中可用。
【解决方案3】:

您的 Enum 在 3 个地方失去了排序。首先,类主体上的属性存储在字典中,然后将项目复制到另一个字典中。最后你的 values() 返回第三个字典。字典不保存排序,无法获取类体内属性的排序。

有了这个系统,最简单的就是有一个变量

__ordering__ = [ 'single', 'double' ]

并使values() 返回一个元组列表(如dict.items())。

class EnumMeta(type):
    def __new__(typ, name, bases, attrs):
        cls_attrs = {}
        cls_choices = {}

        for attr_name, value in attrs.items():
            cls_attrs[attr_name] = attr_name.lower()
            if not attr_name.startswith("__"):
                cls_choices[attr_name.lower()] = value

        ordering = attrs.get('__ordering__')
        if ordering == None:
            ordering = sorted(cls_choices.keys())

        def choices(cls):
            return dict(cls_choices)

        def values(cls, value=None):
            if value is None:
                return [ (k, cls_choices[k] ) for k in ordering ]
            elif not isinstance(value, basestring):
                return [ (k, cls_choices[k] ) for k in value ]
            else:
                return unicode(cls_choices.get(value))

        def keys(cls, nil=False):
            items = list(ordering)
            if nil:
                items.append('')

            return items

        def combined_length(cls):
            return len(",".join(cls.values().keys()))

        def max_length(cls):
            return max(map(len, cls.values().keys()))

        cls_attrs['choices'] = classmethod(choices)
        cls_attrs['values'] = classmethod(values)
        cls_attrs['keys'] = classmethod(keys)
        cls_attrs['combined_length'] = classmethod(combined_length)
        cls_attrs['max_length'] = classmethod(max_length)

        return type(name, bases, cls_attrs)

class SideHemType:
    __ordering__ = ['double', 'single']
    __metaclass__ = EnumMeta

    Single = "Single side hem for opaque fabrics"
    Double = "Double side hem for transparent fabrics"


print SideHemType.keys()
print SideHemType.values()

【讨论】:

  • 添加Enum排序没有效果,看我的例子。
  • @Robert __ordering__ 并不神奇,Antti 还修改了 EnumMeta 的代码,如果你只添加 __ordering__ 而不使用他的 EnumMeta 代码,它将无济于事。
猜你喜欢
  • 2011-08-29
  • 2014-07-12
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
  • 2015-08-08
  • 2013-07-10
  • 2012-03-10
相关资源
最近更新 更多