継承 - python enum 複数




Pythonで 'Enum'をどのように表現できますか? (20)

2013-05-10で、GuidoはPEP 435をPython 3.4標準ライブラリに受け入れることに合意しました。 これはPythonがついに列挙をサポートしていることを意味します!

Python 3.3,3.2,3.1,2.7,2.6,2.5、および2.4用のバックポートがあります。 これはPypiのenum34です。

宣言:

>>> from enum import Enum
>>> class Color(Enum):
...     red = 1
...     green = 2
...     blue = 3

表現:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>

反復:

>>> for color in Color:
...   print(color)
...
Color.red
Color.green
Color.blue

プログラムによるアクセス:

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue

詳細については、提案を参照しください。 おそらくすぐに公式の文書が続きます。

私は主にC#開発者ですが、私は現在Pythonでプロジェクトに取り組んでいます。

Pythonで列挙型の等価を表現するにはどうすればよいですか?


Enumクラスは1つのライナーにすることができます。

class Enum(tuple): __getattr__ = tuple.index

それを使用する方法(順方向および逆方向の検索、キー、値、アイテムなど)

>>> State = Enum(['Unclaimed', 'Claimed'])
>>> State.Claimed
1
>>> State[1]
'Claimed'
>>> State
('Unclaimed', 'Claimed')
>>> range(len(State))
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]
[('Unclaimed', 0), ('Claimed', 1)]

PEP 435以前は、Pythonには同等の機能がありませんでしたが、独自に実装することもできます。

自分自身、私はそれをシンプルに保つのが好きです(私はネット上でひどく複雑な例を見たことがあります)。

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4( PEP 435 )では、 Enum基本クラスにすることができます。 これはPEPで説明されているように、少し余分な機能を提供します。 たとえば、enumメンバーは整数とは異なり、 namevalue構成されvalue

class Animal(Enum):
    DOG = 1
    CAT = 2

print(Animal.DOG)
# <Animal.DOG: 1>

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

値を入力しない場合は、次のショートカットを使用します。

class Animal(Enum):
    DOG, CAT = range(2)

Enum実装はリストに変換でき、反復可能です。 そのメンバーの順序は宣言の順序であり、その値とは関係ありません。 例えば:

class Animal(Enum):
    DOG = 1
    CAT = 2
    COW = 0

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True

Python 3.4からenumの公式サポートがあります。 Python 3.4のドキュメントページでは、ここでドキュメントや例を見つけることができます

列挙は、クラス構文を使用して作成されます。これにより、読み書きが容易になります。 別の作成方法については、「Functional API」を参照してください。 列挙型を定義するには、次のように列挙型をサブクラス化します。

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3

Pythonの新しい標準はPEP 435です 。そのため、EnumクラスはPythonの将来のバージョンで利用可能になります:

>>> from enum import Enum

しかし、これを使い始めるには、PEPを動機としたオリジナルのライブラリをインストールすることができます:

#sudo pip install flufl.enum   //or #sudo easy_install flufl.enum

その後、オンラインガイドに従って使用することできます

>>> from flufl.enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue

davidgはdictsの使用を推奨しています。 私はさらに一歩進んでセットを使用します:

months = set('January', 'February', ..., 'December')

これで、値がセット内の値の1つにこのように一致するかどうかをテストできます:

if m in months:

dFのように、私は通常列挙型の代わりに文字列定数を使用します。


PyPIのenumパッケージは、enumの堅牢な実装を提供します。 以前の答えはPEP 354を述べました。 これは拒否されましたが、提案はhttp://pypi.python.org/pypi/enumに実装されました。

使い方は簡単でエレガントです:

>>> from enum import Enum
>>> Colors = Enum('red', 'blue', 'green')
>>> shirt_color = Colors.green
>>> shirt_color = Colors[2]
>>> shirt_color > Colors.red
True
>>> shirt_color.index
2
>>> str(shirt_color)
'green'

PEP 435で説明されているように、列挙型がPython 3.4に追加されました。 ピピで3.3,3.2,3.1,2.7,2.6,2.5,2.4にバックポートされています。

より高度なEnumテクニックについては、 aenumライブラリを試してみましょう( 2.7,3.3+ 、enum34と同じ作者、py2とpy3の間に完全な互換性はありません。例えばPython 2では__order__が必要__order__ )。

  • enum34を使用するには、 $ pip install enum34
  • aenumを使用するには、 $ pip install aenum

enum (数字なし)をインストールすると、完全に異なる互換性のないバージョンがインストールされます。

from enum import Enum     # for enum34, or the stdlib version
# from aenum import Enum  # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')

Animal.ant  # returns <Animal.ant: 1>
Animal['ant']  # returns <Animal.ant: 1> (string lookup)
Animal.ant.name  # returns 'ant' (inverse lookup)

または同等に:

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

以前のバージョンでは、列挙型を達成する方法の1つは次のとおりです。

def enum(**enums):
    return type('Enum', (), enums)

これは次のように使用されます:

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'

また、次のような方法で簡単に自動列挙をサポートすることもできます。

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

次のように使用します。

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1

値を名前に変換するサポートは、次のように追加できます。

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)

これはその名前のものをすべて上書きしますが、enumを出力にレンダリングすると便利です。 逆マッピングが存在しない場合、KeyErrorがスローされます。 最初の例では:

>>> Numbers.reverse_mapping['three']
'THREE'

うーん...私はエニュムに最も近いものは、次のように定義された辞書であると考えます:

months = {
    'January': 1,
    'February': 2,
    ...
}

または

months = dict(
    January=1,
    February=2,
    ...
)

次に、このような定数にシンボリック名を使うことができます:

mymonth = months['January']

タプルのリストやタプルのタプルのような他のオプションもありますが、値にアクセスするための "シンボリック"(定数文字列)の方法を提供する唯一の辞書です。

編集:私もアレクサンドルの答えが好きです!


ここに1つの実装があります:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

使用方法は次のとおりです。

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)

これは私が見た中で最高のものです: "Pythonのファーストクラス列挙型"

これはあなたにクラスを与え、クラスにはすべての列挙型が含まれます。 列挙型は互いに比較できますが、特別な価値はありません。 それらを整数値として使用することはできません。 (私は整数値であるCのenumに慣れていたので、最初はこれに抵抗しました。しかし、整数として使うことができないのであれば、間違って整数として使うことはできません。各列挙型は一意の値です。 列挙型を印刷したり、それらを反復処理したりすることができます。列挙型の値が列挙型であることをテストできます。 それはかなり完全で滑らかです。

編集(cfi):上記のリンクはPython 3と互換性がありません。 Python 3への私のenum.pyポートは次のとおりです:

def cmp(a,b):
   if a < b: return -1
   if b < a: return 1
   return 0


def Enum(*names):
   ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!

   class EnumClass(object):
      __slots__ = names
      def __iter__(self):        return iter(constants)
      def __len__(self):         return len(constants)
      def __getitem__(self, i):  return constants[i]
      def __repr__(self):        return 'Enum' + str(names)
      def __str__(self):         return 'enum ' + str(constants)

   class EnumValue(object):
      __slots__ = ('__value')
      def __init__(self, value): self.__value = value
      Value = property(lambda self: self.__value)
      EnumType = property(lambda self: EnumType)
      def __hash__(self):        return hash(self.__value)
      def __cmp__(self, other):
         # C fans might want to remove the following assertion
         # to make all enums comparable by ordinal value {;))
         assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
         return cmp(self.__value, other.__value)
      def __lt__(self, other):   return self.__cmp__(other) < 0
      def __eq__(self, other):   return self.__cmp__(other) == 0
      def __invert__(self):      return constants[maximum - self.__value]
      def __nonzero__(self):     return bool(self.__value)
      def __repr__(self):        return str(names[self.__value])

   maximum = len(names) - 1
   constants = [None] * len(names)
   for i, each in enumerate(names):
      val = EnumValue(i)
      setattr(EnumClass, each, val)
      constants[i] = val
   constants = tuple(constants)
   EnumType = EnumClass()
   return EnumType


if __name__ == '__main__':
   print( '\n*** Enum Demo ***')
   print( '--- Days of week ---')
   Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
   print( Days)
   print( Days.Mo)
   print( Days.Fr)
   print( Days.Mo < Days.Fr)
   print( list(Days))
   for each in Days:
      print( 'Day:', each)
   print( '--- Yes/No ---')
   Confirmation = Enum('No', 'Yes')
   answer = Confirmation.No
   print( 'Your answer is not', ~answer)

だから、私は同意する。 Pythonでは型の安全性を強制しませんが、私は愚かな間違いから自分自身を守りたいと思います。 では、これについてどう思いますか?

class Animal(object):
    values = ['Horse','Dog','Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

それは私の列挙を定義する際に価値の衝突から私を保つ。

>>> Animal.Cat
2

本当に高速逆引き参照:もう一つの便利な利点があります:

def name_of(self, i):
    return self.values[i]

列挙型のためのクラス定数の使用に関するAlexandruの提案は非常にうまくいきます。

また、人間が読める文字列表現を検索するために、各定数セットの辞書を追加するのも好きです。

これは2つの目的を果たします:a)enumを簡単に印刷する簡単な方法を提供し、b)辞書は論理的に定数をグループ化し、メンバーシップをテストできます。

class Animal:    
  TYPE_DOG = 1
  TYPE_CAT = 2

  type2str = {
    TYPE_DOG: "dog",
    TYPE_CAT: "cat"
  }

  def __init__(self, type_):
    assert type_ in self.type2str.keys()
    self._type = type_

  def __repr__(self):
    return "<%s type=%s>" % (
        self.__class__.__name__, self.type2str[self._type].upper())

数値が必要な場合は、ここで最も簡単です。

dog, cat, rabbit = range(3)

Python 3.xでは、末尾に星印を付けたプレースホルダを追加することもできます。これにより、メモリーを無駄にすることがなく、数えられない場合に備えて、残りの範囲の値を吸収します:

dog, cat, rabbit, horse, *_ = range(100)

私が使っているもの:

class Enum(object):
    def __init__(self, names, separator=None):
        self.names = names.split(separator)
        for value, name in enumerate(self.names):
            setattr(self, name.upper(), value)
    def tuples(self):
        return tuple(enumerate(self.names))

使い方:

>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))

これは、state.PUBLISHEDのような整数定数と、Djangoモデルの選択肢として使う2つのタプルを提供します。


私が価値のあるいくつかの異なる特性を持つアプローチがあります:

  • allow>と<比較は列挙型ではなく、語彙順で行われる
  • xa、x ['a']またはx [0]で名前、
  • [:]または[ - 1]のようなスライシング操作をサポートします。

最も重要なのは、異なるタイプの列挙型の間の比較を防ぐことです!

密接にhttp://code.activestate.com/recipes/413486-first-class-enums-in-python基づいていhttp://code.activestate.com/recipes/413486-first-class-enums-in-python

このアプローチについて異なる点を説明するために、ここには多くのdoctestが含まれています。

def enum(*names):
    """
SYNOPSIS
    Well-behaved enumerated type, easier than creating custom classes

DESCRIPTION
    Create a custom type that implements an enumeration.  Similar in concept
    to a C enum but with some additional capabilities and protections.  See
    http://code.activestate.com/recipes/413486-first-class-enums-in-python/.

PARAMETERS
    names       Ordered list of names.  The order in which names are given
                will be the sort order in the enum type.  Duplicate names
                are not allowed.  Unicode names are mapped to ASCII.

RETURNS
    Object of type enum, with the input names and the enumerated values.

EXAMPLES
    >>> letters = enum('a','e','i','o','u','b','c','y','z')
    >>> letters.a < letters.e
    True

    ## index by property
    >>> letters.a
    a

    ## index by position
    >>> letters[0]
    a

    ## index by name, helpful for bridging string inputs to enum
    >>> letters['a']
    a

    ## sorting by order in the enum() create, not character value
    >>> letters.u < letters.b
    True

    ## normal slicing operations available
    >>> letters[-1]
    z

    ## error since there are not 100 items in enum
    >>> letters[99]
    Traceback (most recent call last):
        ...
    IndexError: tuple index out of range

    ## error since name does not exist in enum
    >>> letters['ggg']
    Traceback (most recent call last):
        ...
    ValueError: tuple.index(x): x not in tuple

    ## enums must be named using valid Python identifiers
    >>> numbers = enum(1,2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: Enum values must be string or unicode

    >>> a = enum('-a','-b')
    Traceback (most recent call last):
        ...
    TypeError: Error when calling the metaclass bases
        __slots__ must be identifiers

    ## create another enum
    >>> tags = enum('a','b','c')
    >>> tags.a
    a
    >>> letters.a
    a

    ## can't compare values from different enums
    >>> letters.a == tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    >>> letters.a < tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    ## can't update enum after create
    >>> letters.a = 'x'
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'a' is read-only

    ## can't update enum after create
    >>> del letters.u
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'u' is read-only

    ## can't have non-unique enum values
    >>> x = enum('a','b','c','a')
    Traceback (most recent call last):
        ...
    AssertionError: Enums must not repeat values

    ## can't have zero enum values
    >>> x = enum()
    Traceback (most recent call last):
        ...
    AssertionError: Empty enums are not supported

    ## can't have enum values that look like special function names
    ## since these could collide and lead to non-obvious errors
    >>> x = enum('a','b','c','__cmp__')
    Traceback (most recent call last):
        ...
    AssertionError: Enum values beginning with __ are not supported

LIMITATIONS
    Enum values of unicode type are not preserved, mapped to ASCII instead.

    """
    ## must have at least one enum value
    assert names, 'Empty enums are not supported'
    ## enum values must be strings
    assert len([i for i in names if not isinstance(i, types.StringTypes) and not \
        isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'
    ## enum values must not collide with special function names
    assert len([i for i in names if i.startswith("__")]) == 0,\
        'Enum values beginning with __ are not supported'
    ## each enum value must be unique from all others
    assert names == uniquify(names), 'Enums must not repeat values'

    class EnumClass(object):
        """ See parent function for explanation """

        __slots__ = names

        def __iter__(self):
            return iter(constants)

        def __len__(self):
            return len(constants)

        def __getitem__(self, i):
            ## this makes xx['name'] possible
            if isinstance(i, types.StringTypes):
                i = names.index(i)
            ## handles the more normal xx[0]
            return constants[i]

        def __repr__(self):
            return 'enum' + str(names)

        def __str__(self):
            return 'enum ' + str(constants)

        def index(self, i):
            return names.index(i)

    class EnumValue(object):
        """ See parent function for explanation """

        __slots__ = ('__value')

        def __init__(self, value):
            self.__value = value

        value = property(lambda self: self.__value)

        enumtype = property(lambda self: enumtype)

        def __hash__(self):
            return hash(self.__value)

        def __cmp__(self, other):
            assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'
            return cmp(self.value, other.value)

        def __invert__(self):
            return constants[maximum - self.value]

        def __nonzero__(self):
            ## return bool(self.value)
            ## Original code led to bool(x[0])==False, not correct
            return True

        def __repr__(self):
            return str(names[self.value])

    maximum = len(names) - 1
    constants = [None] * len(names)
    for i, each in enumerate(names):
        val = EnumValue(i)
        setattr(EnumClass, each, val)
        constants[i] = val
    constants = tuple(constants)
    enumtype = EnumClass()
    return enumtype

私は本当にAlec Thomasのソリューションが好きです(http://.com/a/1695250)。

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)

これはエレガントでクリーンな見た目ですが、指定された属性を持つクラスを作成する単なる関数です。

関数を少し変更するだけで、もう少し "enumy"を実行することができます:

注:pygtkの新しいスタイルの 'enums'(Gtk.MessageType.WARNINGなど)の動作を再現しようとして、次の例を作成しました。

def enum_base(t, **enums):
    '''enums with a base class'''
    T = type('Enum', (t,), {})
    for key,val in enums.items():
        setattr(T, key, T(val))

    return T

これは、指定された型に基づいて列挙型を作成します。 前の関数のように属性アクセスを与えることに加えて、型に関してはEnum toが期待するように動作します。 また、基底クラスも継承します。

たとえば、整数の列挙型は次のようになります。

>>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3)
>>> Numbers.ONE
1
>>> x = Numbers.TWO
>>> 10 + x
12
>>> type(Numbers)
<type 'type'>
>>> type(Numbers.ONE)
<class 'Enum'>
>>> isinstance(x, Numbers)
True

このメソッドで行うことができる別の興味深いことは、組み込みメソッドをオーバーライドすることによって特定の動作をカスタマイズすることです。

def enum_repr(t, **enums):
    '''enums with a base class and repr() output'''
    class Enum(t):
        def __repr__(self):
            return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)

    for key,val in enums.items():
        i = Enum(val)
        i._name = key
        setattr(Enum, key, i)

    return Enum



>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)
>>> repr(Numbers.ONE)
'<enum ONE of type Enum(int)>'
>>> str(Numbers.ONE)
'1'

複雑にしないでおく:

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList

    def __getattr__(self, name):
            return self.tupleList.index(name)

次に:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1

私はバイナリ演算子の左右の結合性を表現するために、pyparsingでいくつかの象徴的な定数が必要でした。私はこのようなクラス定数を使用しました:

# an internal class, not intended to be seen by client code
class _Constants(object):
    pass


# an enumeration of constants for operator associativity
opAssoc = _Constants()
opAssoc.LEFT = object()
opAssoc.RIGHT = object()

クライアントコードでこれらの定数を使用する場合は、以下を使用して列挙型全体をインポートできます。

import opAssoc from pyparsing

列挙型は一意であり、 '=='ではなく 'is'でテストすることができます。マイナーコンセプトのためにコード内で大きなフットプリントを占めず、クライアントコードに簡単にインポートできます。彼らは派手なstr()の動作をサポートしていませんが、これまでのところYAGNIカテゴリにあります。


def M_add_class_attribs(attribs):
    def foo(name, bases, dict_):
        for v, k in attribs:
            dict_[k] = v
        return type(name, bases, dict_)
    return foo

def enum(*names):
    class Foo(object):
        __metaclass__ = M_add_class_attribs(enumerate(names))
        def __setattr__(self, name, value):  # this makes it read-only
            raise NotImplementedError
    return Foo()

このように使用してください:

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError

一意のシンボルだけを必要とし、値について気にしない場合は、次の行を置き換えます。

__metaclass__ = M_add_class_attribs(enumerate(names))

これとともに:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)




enums