具有重复值的Python枚举

时间:2015-07-21 11:05:26

标签: python enums

我在使用Enum时遇到问题,其中一些属性具有相同的值。我认为Enums对python来说是新手,我找不到任何其他对此问题的引用。无论如何,假设我有以下

class CardNumber(Enum):
    ACE      = 11
    TWO      = 2
    THREE    = 3
    FOUR     = 4
    FIVE     = 5
    SIX      = 6
    SEVEN    = 7
    EIGHT    = 8
    NINE     = 9
    TEN      = 10
    JACK     = 10
    QUEEN    = 10
    KING     = 10

显然,这些是黑色插孔中的卡号及其对应值。十通王具有相同的价值。但如果我执行print(CardNumber.QUEEN)之类的操作,我会回来<CardNumber.TEN: 10>。更重要的是,如果我迭代这些,它只是迭代唯一值。

>>> for elem in CardNumber:
...     print(elem)
CardNumber.ACE
CardNumber.TWO
CardNumber.THREE
CardNumber.FOUR
CardNumber.FIVE
CardNumber.SIX
CardNumber.SEVEN
CardNumber.EIGHT
CardNumber.NINE
CardNumber.TEN

如何解决这个问题?我希望CardNumber.QUEEN和CardNumber.TEN是唯一的,并且都出现在任何迭代中。我唯一能想到的就是给每个属性一个第二个值,它可以作为一个独特的id,但这似乎是unpythonic。

5 个答案:

答案 0 :(得分:4)

更新

使用(aenum)](https://pypi.python.org/pypi/aenum 1 您有几个选择:

  • 改为使用NamedConstant:不提供任何Enum个额外内容(迭代,查找等)[请参阅:以下原始答案]

  • 使用NoAlias:具有所有正常的Enum行为,除了每个成员都是唯一的,并且按值查找不可用

NoAlias的一个例子:

from aenum import Enum, NoAlias

class CardNumber(Enum):

    _order_ = 'EIGHT NINE TEN JACK QUEEN KING ACE'  # only needed for Python 2.x
    _settings_ = NoAlias

    EIGHT    = 8
    NINE     = 9
    TEN      = 10
    JACK     = 10
    QUEEN    = 10
    KING     = 10
    ACE      = 11

并在使用中:

>>> list(CardNumber)
[<CardNumber.EIGHT: 8>, <CardNumber.NINE: 9>, <CardNumber.TEN: 10>, <CardNumber.JACK: 10>, <CardNumber.QUEEN: 10>, <CardNumber.KING: 10>, <CardNumber.ACE: 11>]

>>> CardNumber.QUEEN == CardNumber.KING
False

>>> CardNumber.QUEEN is CardNumber.KING
False

>>> CardNumber.QUEEN.value == CardNumber.KING.value
True

>>> CardNumber(8)
Traceback (most recent call last):
  ...
TypeError: NoAlias enumerations cannot be looked up by value

原始答案

如果您想要命名常量而不关心Enum的其他功能,可以使用aenum library中的NamedConstant类:

from aenum import NamedConstant

class CardNumber(NamedConstant):
    ACE      = 11
    TWO      = 2
    THREE    = 3
    FOUR     = 4
    FIVE     = 5
    SIX      = 6
    SEVEN    = 7
    EIGHT    = 8
    NINE     = 9
    TEN      = 10
    JACK     = 10
    QUEEN    = 10
    KING     = 10

重复值仍然不同:

>>> CardNumber.TEN is CardNumber.JACK
False

>>> CardNumber.TEN == CardNumber.JACK
True

>>> CardNumber.TEN == 10
True

1 披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum)图书馆的作者。

答案 1 :(得分:2)

是的,具有重复值的标签将变为第一个此类标签的别名。

您可以枚举7474774 (<= value of user_id),ODFLKGJDFLKGJ(<= value of product) 属性,它是一个包含别名的有序字典:

__members__

但是,如果必须具有唯一的标签和值对(而不是别名),那么>>> for name, value in CardNumber.__members__.items(): ... print(name, value) ... ACE CardNumber.ACE TWO CardNumber.TWO THREE CardNumber.THREE FOUR CardNumber.FOUR FIVE CardNumber.FIVE SIX CardNumber.SIX SEVEN CardNumber.SEVEN EIGHT CardNumber.EIGHT NINE CardNumber.NINE TEN CardNumber.TEN JACK CardNumber.TEN QUEEN CardNumber.TEN KING CardNumber.TEN 是错误的方法;它与纸牌游戏的用例不匹配。

在这种情况下,最好使用字典(如果订单也很重要,请考虑使用enum.Enum。)

答案 2 :(得分:0)

感谢这里的答案,但对于使用enum.auto()的人,这里是具有别名或重复的枚举值的扩展答案

import enum
PREDICT_ALIAS = enum.auto()

class Mode(enum.Enum):
    FIT = enum.auto()
    EVALUATE = enum.auto()
    PREDICT = PREDICT_ALIAS 
    INFER = PREDICT_ALIAS # alias mode

print(Mode.PREDICT is Mode.INFER)  # return True :)

答案 3 :(得分:0)

对于那些仍不考虑使用aenumOrderedDict的人,仍然可以四处逛逛:

import enum

class AliasedEnum(enum.Enum):
    def __init_subclass__(cls, *kargs, **kwargs):
        import inspect

        # unfortunately, there's no cleaner ways to retrieve original members
        for stack in reversed(inspect.stack()):
            frame_locals = stack[0].f_locals
            enum_members = frame_locals.get('enum_members')
            if enum_members is None:
                try:
                    enum_members = frame_locals['classdict']._member_names
                except (KeyError, AttributeError):
                    continue
            break
        else:
            raise RuntimeError('Unable to checkout AliasedEnum members!')

        # patch subclass __getattribute__ to evade deduplication checks
        cls._shield_members = list(enum_members)
        cls._shield_getattr = cls.__getattribute__
        def patch(self, key):
            if key.startswith('_shield_'):
                return object.__getattribute__(self, key)
            if key.startswith('_value'):
                if not hasattr(self, '_name_'):
                    self._shield_counter = 0
                elif self._shield_counter < self._shield_members.index(self._name_):
                    self._shield_counter += 1
                    class unequal:
                        pass
                    return unequal
            return self._shield_getattr(key)
        cls.__getattribute__ = patch

class Fck(AliasedEnum):
    A = 0
    B = 0
    C = 0

这依赖于以下事实:标准enum.EnumMeta.__new__使用O(n²)算法进行重复数据删除以处理不可散列的值,因此应将其视为不良做法。它仍然可以达到预期的效果:

$ python -i aliased_enum.py
>>> Fck.A
<Fck.A: 0>
>>> Fck.B
<Fck.B: 0>
>>> Fck.C
<Fck.C: 0>

已通过Python3.7.4测试,并且可以在cpython标准库中移植。

答案 4 :(得分:0)

一种简单的方法是将值放在对象内:

C:\Users\Devesh\ShoppingList>react-native run-android
info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.
Jetifier found 967 file(s) to forward-jetify. Using 4 workers...
info Starting JS server...
info Installing the app...

FAILURE: Build failed with an exception.

* What went wrong:
Could not initialize class org.codehaus.groovy.runtime.InvokerHelper

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 864ms

error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: gradlew.bat app:installDebug -PreactNativeDevServerPort=8081

FAILURE: Build failed with an exception.

* What went wrong:
Could not initialize class org.codehaus.groovy.runtime.InvokerHelper

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 864ms

    at makeError (C:\Users\Devesh\ShoppingList\node_modules\execa\index.js:174:9)
    at C:\Users\Devesh\ShoppingList\node_modules\execa\index.js:278:16
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async runOnAllDevices (C:\Users\Devesh\ShoppingList\node_modules\@react-native-community\cli-platform-android\build\commands\runAndroid\runOnAllDevices.js:94:5)
    at async Command.handleAction (C:\Users\Devesh\ShoppingList\node_modules\react-native\node_modules\@react-native-community\cli\build\index.js:186:9)

顺便说一句,不要对Value类使用@dataclass装饰器。数据类比较其属性的值以检查对象是否相等,这意味着它的行为与默认的Enum完全一样!