1143

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

Python で Enum に相当するものを表すにはどうすればよいですか?

4

43 に答える 43

2901

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

より高度な Enum テクニックについては、aenum ライブラリ(2.7、3.3+、 と同じ作成者enum34。コードは py2 と py3 の間で完全に互換性がありません。たとえば__order__、 python 2 で必要になります) を試してください。

  • を使用する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)

これにより、その名前を持つものはすべて上書きされますが、出力で列挙型をレンダリングするのに役立ちます。KeyError逆マッピングが存在しない場合は、がスローされます。最初の例では:

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

MyPy を使用している場合、「列挙型」を表現する別の方法はtyping.Literal.

例えば:

from typing import Literal #python >=3.8
from typing_extensions import Literal #python 2.7, 3.4-3.7


Animal = Literal['ant', 'bee', 'cat', 'dog']

def hello_animal(animal: Animal):
    print(f"hello {animal}")

hello_animal('rock') # error
hello_animal('bee') # passes

于 2009-11-08T03:15:28.320 に答える
924

PEP 435 より前は、Python には同等のものはありませんでしたが、独自のものを実装できました。

私自身、シンプルに保つのが好きです(ネット上で恐ろしく複雑な例をいくつか見ました)、このようなもの...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4 ( PEP 435 ) では、 Enumを基本クラスにすることができます。これにより、PEP で説明されている追加機能が少し得られます。たとえば、列挙メンバーは整数とは異なり、 anameと aで構成されvalueます。

from enum import Enum

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
于 2008-08-31T16:06:14.360 に答える
349

1 つの実装を次に示します。

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

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

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

print(Animals.DOG)
于 2010-02-02T07:21:46.403 に答える
218

数値が必要な場合は、次の方法が最も簡単です。

dog, cat, rabbit = range(3)

Python 3.xでは、最後にスター付きのプレースホルダーを追加することもできます。これにより、メモリの浪費を気にせず、数えられない場合に備えて、範囲の残りのすべての値が吸収されます。

dog, cat, rabbit, horse, *_ = range(100)
于 2008-08-31T20:31:22.693 に答える
135

最善の解決策は、偽物 に何を求めるかによって異なりますenum

単純な列挙型:

異なるアイテムを識別する名前enumのリストとしてのみ必要な場合は、 Mark Harrison (上記)によるソリューションが最適です。

Pen, Pencil, Eraser = range(0, 3)

a を使用すると、任意の開始値rangeを設定することもできます。

Pen, Pencil, Eraser = range(9, 12)

上記に加えて、項目が何らかのコンテナに属している必要がある場合は、それらをクラスに埋め込みます。

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)

列挙型アイテムを使用するには、コンテナ名とアイテム名を使用する必要があります。

stype = Stationery.Pen

複雑な列挙:

列挙型の長いリストまたは列挙型のより複雑な使用法では、これらのソリューションでは不十分です。Python Cookbookで公開されているPython で列挙型をシミュレートするための Will Ware によるレシピを見ることができます。そのオンライン版はこちらから入手できます。

より詳しい情報:

PEP 354: Enumerations in Pythonには、Python での enum の提案の興味深い詳細と、それが拒否された理由があります。

于 2009-10-07T02:47:33.213 に答える
82

JDK 5 より前の Java で使用されていたタイプセーフな列挙型パターンには、多くの利点があります。Alexandruの回答と同じように、クラスを作成し、クラスレベルのフィールドは列挙値です。ただし、列挙値は小さな整数ではなく、クラスのインスタンスです。これには、列挙型の値が誤って小さな整数と等しいと比較されないという利点があります。出力方法を制御し、便利な場合は任意のメソッドを追加し、isinstance を使用してアサーションを作成できます。

class Animal:
   def __init__(self, name):
       self.name = name

   def __str__(self):
       return self.name

   def __repr__(self):
       return "<Animal: %s>" % self

Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")

>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False

python-devの最近のスレッドでは、次のような enum ライブラリがいくつか存在することが指摘されています。

于 2008-09-01T16:05:25.540 に答える
65

Enum クラスはワンライナーにすることができます。

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)]
于 2012-02-08T20:59:58.027 に答える
54

だから、私は同意します。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]
于 2010-11-03T23:02:54.787 に答える
52

Python には に相当するものが組み込まれておらず、他の回答には独自のものを実装するためのアイデアがあります ( Python クックブックのオーバー ザ トップ バージョンenumにも興味があるかもしれません)。

enumただし、 C で が呼び出される状況では、通常は単純な文字列を使用することになります。オブジェクト/属性の実装方法により、(C)Python はとにかく短い文字列で非常に高速に動作するように最適化されているため、整数を使用することによるパフォーマンス上の利点は実際にはありません。入力ミスや無効な値を防ぐために、選択した場所にチェックを挿入できます。

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(クラスを使用する場合と比較した場合の欠点の 1 つは、オートコンプリートの利点が失われることです)

于 2008-08-31T18:10:50.050 に答える
42

2013 年 5 月 10 日、Guido はPEP 435を Python 3.4 標準ライブラリに受け入れることに同意しました。これは、Python がついに列挙型の組み込みサポートを備えたことを意味します!

Python 3.3、3.2、3.1、2.7、2.6、2.5、および 2.4 で使用できるバックポートがあります。enum34 としてPypiにあります。

宣言:

>>> 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

詳細については、提案を参照してください。公式ドキュメントはおそらくすぐに続きます。

于 2013-05-10T16:09:02.937 に答える
39

私はPythonで列挙型を次のように定義することを好みます:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

整数が一意であることを保証することを心配する必要がないため、整数を使用するよりもバグに強くなります (たとえば、Dog = 1 と Cat = 1 と言った場合、失敗します)。

タイプミスを気にする必要がないため、文字列を使用するよりもバグに強くなります (たとえば、 x == "catt" は黙って失敗しますが、 x == Animal.Catt は実行時例外です)。


補遺: Dog と Cat に適切なメタクラスを持つシンボル クラスから継承させることで、このソリューションを強化することもできます。

class SymbolClass(type):
    def __repr__(self): return self.__qualname__
    def __str__(self): return self.__name__

class Symbol(metaclass=SymbolClass): pass


class Animal:
    class Dog(Symbol): pass
    class Cat(Symbol): pass

次に、これらの値を使用して、たとえば辞書のインデックスを作成する場合、その表現を要求すると、それらが適切に表示されます。

>>> mydict = {Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
>>> mydict
{Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
于 2010-11-29T02:05:55.923 に答える
34
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)
于 2008-09-20T11:49:38.617 に答える
30

Python 3.4 から、enum が公式にサポートされました。Python 3.4 ドキュメンテーション ページ でドキュメンテーションと例を見つけることができます。

列挙はクラス構文を使用して作成されるため、読み書きが容易になります。代替の作成方法は Functional API で説明されています。列挙型を定義するには、次のように Enum をサブクラス化します。

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3
于 2013-12-11T13:49:01.567 に答える
29

Another, very simple, implementation of an enum in Python, using namedtuple:

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

or, alternatively,

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())




# Example for dictionary param:
values = {"Salad": 20, "Carrot": 99, "Tomato": "No i'm not"} 
Vegetables= enum(**values)

# >>> print(Vegetables.Tomato)        'No i'm not'


# Example for keyworded params: 
Fruits = enum(Apple="Steve Jobs", Peach=1, Banana=2)

# >>> print(Fruits.Apple)             'Steve Jobs'

Like the method above that subclasses set, this allows:

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO

But has more flexibility as it can have different keys and values. This allows

MyEnum.FOO < MyEnum.BAR

to act as is expected if you use the version that fills in sequential number values.

于 2011-08-07T05:51:01.670 に答える
24

うーん...列挙型に最も近いものは、次のように定義された辞書であると思います。

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

また

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

次に、次のように定数に記号名を使用できます。

mymonth = months['January']

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

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

于 2008-08-31T16:09:53.633 に答える
20

私が使用するもの:

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 つのタプルが得られます。

于 2009-02-02T23:39:53.443 に答える
19

古い Python 2.x を使用して、シンプルに保ちます (Python 3 については以下を参照してください!)。

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

Python 3を使用するときは、シンプルに保ちます。

from enum import Enum
class MyEnum(Enum):
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4

それで:

MyEnum.DOWN

参照: https://docs.python.org/3/library/enum.html

于 2014-03-28T21:44:30.093 に答える
18

Python の標準はPEP 435であるため、Enum クラスは Python 3.4 以降で使用できます。

>>> from enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue
于 2013-05-10T16:22:42.553 に答える
17

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

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

次のように、値がセット内の値のいずれかと一致するかどうかをテストできます。

if m in months:

ただし、dF と同様に、通常は列挙型の代わりに文字列定数を使用します。

于 2008-09-02T03:20:30.847 に答える
16

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

http://code.activestate.com/recipes/413486/

クラスが提供され、クラスにはすべての列挙型が含まれます。列挙型は互いに比較できますが、特定の値はありません。これらを整数値として使用することはできません。(私は整数値であるC列挙型に慣れているので、最初はこれに抵抗しました。しかし、整数として使用できない場合は、誤って整数として使用できないため、全体的には勝利だと思います.) 各列挙型は一意の値です。列挙型を出力したり、それらを反復したり、列挙値が列挙型の「中に」あることをテストしたりできます。それはかなり完全で滑らかです。

編集 (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)
于 2009-11-18T02:51:40.913 に答える
15

バイナリファイル形式をデコードするために、Enumクラスが必要になることがありました。私がたまたま欲しかった機能は、簡潔な列挙型の定義、整数値または文字列のいずれかによって列挙型のインスタンスを自由に作成する機能、および便利なrepr表現です。これが私が最終的に得たものです:

>>> class Enum(int):
...     def __new__(cls, value):
...         if isinstance(value, str):
...             return getattr(cls, value)
...         elif isinstance(value, int):
...             return cls.__index[value]
...     def __str__(self): return self.__name
...     def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
...     class __metaclass__(type):
...         def __new__(mcls, name, bases, attrs):
...             attrs['__slots__'] = ['_Enum__name']
...             cls = type.__new__(mcls, name, bases, attrs)
...             cls._Enum__index = _index = {}
...             for base in reversed(bases):
...                 if hasattr(base, '_Enum__index'):
...                     _index.update(base._Enum__index)
...             # create all of the instances of the new class
...             for attr in attrs.keys():
...                 value = attrs[attr]
...                 if isinstance(value, int):
...                     evalue = int.__new__(cls, value)
...                     evalue._Enum__name = attr
...                     _index[value] = evalue
...                     setattr(cls, attr, evalue)
...             return cls
... 

それを使用する気まぐれな例:

>>> class Citrus(Enum):
...     Lemon = 1
...     Lime = 2
... 
>>> Citrus.Lemon
Citrus.Lemon
>>> 
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
...     Apple = 3
...     Banana = 4
... 
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True

主な機能:

  • str()int()およびrepr()すべてが、可能な限り最も有用な出力を生成します。それぞれ、列挙型の名前、その整数値、および列挙型に戻って評価されるPython式です。
  • コンストラクターによって返される列挙値は、事前定義された値に厳密に制限されており、偶発的な列挙値はありません。
  • 列挙値はシングルトンです。それらは厳密に比較することができますis
于 2011-12-22T02:16:04.487 に答える
11

古い Python 2.x の場合

def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

名前を付けると問題になりますが、値の代わりにオブジェクトを作成しないと、次のことが可能になります。

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

ここにある他の実装を使用する場合 (私の例で名前付きインスタンスを使用する場合も)、異なる列挙型のオブジェクトを比較しようとしないことを確認する必要があります。考えられる落とし穴は次のとおりです。

>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True

うわぁ!

于 2014-11-11T09:26:34.370 に答える
10

私はAlecThomasのソリューション(http://stackoverflow.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

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

たとえば、整数列挙型:

>>> 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

このメソッドで実行できるもう1つの興味深い点は、組み込みメソッドをオーバーライドして特定の動作をカスタマイズすることです。

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'
于 2012-01-18T06:09:18.720 に答える
7

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'
于 2010-03-16T22:36:42.283 に答える
6

これは、私が価値があると思ういくつかの異なる特性を持つアプローチです。

  • 字句順ではなく、列挙型の順序に基づく > と < の比較を許可します
  • 名前、プロパティ、またはインデックスでアイテムをアドレス指定できます: xa、x['a'] または x[0]
  • [:] や [-1] などのスライス操作をサポート

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

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

このアプローチの違いを説明するために、ここには多くのドキュメントテストが含まれています。

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
于 2013-06-19T21:30:47.620 に答える
5

列挙型にクラス定数を使用するという Alexandru の提案は、非常にうまく機能します。

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

これには 2 つの目的があります。a) 列挙型をきれいに出力する簡単な方法を提供し、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())
于 2008-09-19T03:37:43.437 に答える
4

以下を使用してください。

TYPE = {'EAN13':   u'EAN-13',
        'CODE39':  u'Code 39',
        'CODE128': u'Code 128',
        'i25':     u'Interleaved 2 of 5',}

>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']

私はそれをDjangoモデルの選択に使用しましたが、非常にPythonのように見えます。これは実際には列挙型ではありませんが、機能します。

于 2009-10-19T10:21:39.010 に答える
4

元の列挙型の提案であるPEP 354は何年も前に拒否されましたが、それは何度も出てきています。ある種の列挙型が 3.2 に追加される予定でしたが、3.3 に戻されて忘れられました。そして今、Python 3.4 に含める予定のPEP 435があります。PEP 435 のリファレンス実装はflufl.enum.

2013 年 4 月の時点で、人々がその「何か」がどうあるべきかについて合意できる限り、3.4 で何かを標準ライブラリに追加する必要があるという一般的なコンセンサスがあるようです。それが難しい部分です。ここここで始まるスレッド、および 2013 年の初めの 6 つの他のスレッドを参照してください。

一方、これが登場するたびに、PyPI や ActiveState などに多数の新しい設計と実装が登場するため、FLUFL の設計が気に入らない場合は、PyPI 検索を試してください。

于 2013-04-19T01:16:20.590 に答える
4

回答のリストにこれが表示されませんでした。これが私が作成したものです。'in' キーワードと len() メソッドを使用できます。

class EnumTypeError(TypeError):
    pass

class Enum(object):
    """
    Minics enum type from different languages
    Usage:
    Letters = Enum(list('abc'))
    a = Letters.a
    print(a in Letters) # True
    print(54 in Letters) # False
    """
    def __init__(self, enums):
        if isinstance(enums, dict):
            self.__dict__.update(enums)
        elif isinstance(enums, list) or isinstance(enums, tuple):
            self.__dict__.update(**dict((v,k) for k,v in enumerate(enums)))
        else:
            raise EnumTypeError

    def __contains__(self, key):
        return key in self.__dict__.values()

    def __len__(self):
        return len(self.__dict__.values())


if __name__ == '__main__':
    print('Using a dictionary to create Enum:')
    Letters = Enum(dict((v,k) for k,v in enumerate(list('abcde'))))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    print('\nUsing a list to create Enum:')
    Letters = Enum(list('abcde'))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    try:
        # make sure we raise an exception if we pass an invalid arg
        Failure = Enum('This is a Failure')
        print('Failure')
    except EnumTypeError:
        print('Success!')

出力:

Using a dictionary to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5

Using a list to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5
Success!
于 2013-09-05T04:12:03.643 に答える
3

このソリューションは、リストとして定義された列挙型のクラスを取得する簡単な方法です(煩わしい整数の割り当てはもうありません)。

enumeration.py:

import new

def create(class_name, names):
    return new.classobj(
        class_name, (object,), dict((y, x) for x, y in enumerate(names))
    )

example.py:

import enumeration

Colors = enumeration.create('Colors', (
    'red',
    'orange',
    'yellow',
    'green',
    'blue',
    'violet',
))
于 2011-09-18T01:26:54.773 に答える
3

リストまたはセットを列挙として使用するのが好きです。例えば:

>>> packet_types = ['INIT', 'FINI', 'RECV', 'SEND']
>>> packet_types.index('INIT')
0
>>> packet_types.index('FINI')
1
>>>
于 2012-11-20T13:18:46.650 に答える
3

これは、 Alec Thomas のソリューションの変形です。

def enum(*args, **kwargs):
    return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) 

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1
于 2011-06-14T17:29:49.903 に答える
3

面白いことに、先日これが必要になり、使用する価値のある実装が見つからなかったので、自分で書いた:

import functools

class EnumValue(object):
    def __init__(self,name,value,type):
        self.__value=value
        self.__name=name
        self.Type=type
    def __str__(self):
        return self.__name
    def __repr__(self):#2.6 only... so change to what ever you need...
        return '{cls}({0!r},{1!r},{2})'.format(self.__name,self.__value,self.Type.__name__,cls=type(self).__name__)

    def __hash__(self):
        return hash(self.__value)
    def __nonzero__(self):
        return bool(self.__value)
    def __cmp__(self,other):
        if isinstance(other,EnumValue):
            return cmp(self.__value,other.__value)
        else:
            return cmp(self.__value,other)#hopefully their the same type... but who cares?
    def __or__(self,other):
        if other is None:
            return self
        elif type(self) is not type(other):
            raise TypeError()
        return EnumValue('{0.Name} | {1.Name}'.format(self,other),self.Value|other.Value,self.Type)
    def __and__(self,other):
        if other is None:
            return self
        elif type(self) is not type(other):
            raise TypeError()
        return EnumValue('{0.Name} & {1.Name}'.format(self,other),self.Value&other.Value,self.Type)
    def __contains__(self,other):
        if self.Value==other.Value:
            return True
        return bool(self&other)
    def __invert__(self):
        enumerables=self.Type.__enumerables__
        return functools.reduce(EnumValue.__or__,(enum for enum in enumerables.itervalues() if enum not in self))

    @property
    def Name(self):
        return self.__name

    @property
    def Value(self):
        return self.__value

class EnumMeta(type):
    @staticmethod
    def __addToReverseLookup(rev,value,newKeys,nextIter,force=True):
        if value in rev:
            forced,items=rev.get(value,(force,()) )
            if forced and force: #value was forced, so just append
                rev[value]=(True,items+newKeys)
            elif not forced:#move it to a new spot
                next=nextIter.next()
                EnumMeta.__addToReverseLookup(rev,next,items,nextIter,False)
                rev[value]=(force,newKeys)
            else: #not forcing this value
                next = nextIter.next()
                EnumMeta.__addToReverseLookup(rev,next,newKeys,nextIter,False)
                rev[value]=(force,newKeys)
        else:#set it and forget it
            rev[value]=(force,newKeys)
        return value

    def __init__(cls,name,bases,atts):
        classVars=vars(cls)
        enums = classVars.get('__enumerables__',None)
        nextIter = getattr(cls,'__nextitr__',itertools.count)()
        reverseLookup={}
        values={}

        if enums is not None:
            #build reverse lookup
            for item in enums:
                if isinstance(item,(tuple,list)):
                    items=list(item)
                    value=items.pop()
                    EnumMeta.__addToReverseLookup(reverseLookup,value,tuple(map(str,items)),nextIter)
                else:
                    value=nextIter.next()
                    value=EnumMeta.__addToReverseLookup(reverseLookup,value,(str(item),),nextIter,False)#add it to the reverse lookup, but don't force it to that value

            #build values and clean up reverse lookup
            for value,fkeys in reverseLookup.iteritems():
                f,keys=fkeys
                for key in keys:
                    enum=EnumValue(key,value,cls)
                    setattr(cls,key,enum)
                    values[key]=enum
                reverseLookup[value]=tuple(val for val in values.itervalues() if val.Value == value)
        setattr(cls,'__reverseLookup__',reverseLookup)
        setattr(cls,'__enumerables__',values)
        setattr(cls,'_Max',max([key for key in reverseLookup] or [0]))
        return super(EnumMeta,cls).__init__(name,bases,atts)

    def __iter__(cls):
        for enum in cls.__enumerables__.itervalues():
            yield enum
    def GetEnumByName(cls,name):
        return cls.__enumerables__.get(name,None)
    def GetEnumByValue(cls,value):
        return cls.__reverseLookup__.get(value,(None,))[0]

class Enum(object):
    __metaclass__=EnumMeta
    __enumerables__=None

class FlagEnum(Enum):
    @staticmethod
    def __nextitr__():
        yield 0
        for val in itertools.count():
            yield 2**val

def enum(name,*args):
    return EnumMeta(name,(Enum,),dict(__enumerables__=args))

それを取るか、そのままにしておくか、それは私が必要としていたことをしました:)

次のように使用します。

class Air(FlagEnum):
    __enumerables__=('None','Oxygen','Nitrogen','Hydrogen')

class Mammals(Enum):
    __enumerables__=('Bat','Whale',('Dog','Puppy',1),'Cat')
Bool = enum('Bool','Yes',('No',0))
于 2008-10-21T02:08:29.060 に答える
2

私はJava列挙型が好きです。それが Python でのやり方です。

def enum(clsdef):
    class Enum(object):
        __slots__=tuple([var for var in clsdef.__dict__ if isinstance((getattr(clsdef, var)), tuple) and not var.startswith('__')])

        def __new__(cls, *args, **kwargs):
            if not '_the_instance' in cls.__dict__:
                cls._the_instance = object.__new__(cls, *args, **kwargs)
            return cls._the_instance

        def __init__(self):
            clsdef.values=lambda cls, e=Enum: e.values()
            clsdef.valueOf=lambda cls, n, e=self: e.valueOf(n)
            for ordinal, key in enumerate(self.__class__.__slots__):
                args=getattr(clsdef, key)
                instance=clsdef(*args)
                instance._name=key
                instance._ordinal=ordinal
                setattr(self, key, instance)

        @classmethod
        def values(cls):
            if not hasattr(cls, '_values'):
                cls._values=[getattr(cls, name) for name in cls.__slots__]
            return cls._values

        def valueOf(self, name):
            return getattr(self, name)

        def __repr__(self):
            return ''.join(['<class Enum (', clsdef.__name__, ') at ', str(hex(id(self))), '>'])

    return Enum()

サンプル使用:

i=2
@enum
class Test(object):
    A=("a",1)
    B=("b",)
    C=("c",2)
    D=tuple()
    E=("e",3)

    while True:
        try:
            F, G, H, I, J, K, L, M, N, O=[tuple() for _ in range(i)]
            break;
        except ValueError:
            i+=1

    def __init__(self, name="default", aparam=0):
        self.name=name
        self.avalue=aparam

すべてのクラス変数は、コンストラクターと同様にタプルとして定義されます。これまでのところ、名前付き引数は使用できません。

于 2010-06-04T16:33:27.113 に答える
2

Alec Thomas の適切な回答に対するバリアント (列挙値の名前を取得するためのサポート付き) :

class EnumBase(type):
    def __init__(self, name, base, fields):
        super(EnumBase, self).__init__(name, base, fields)
        self.__mapping = dict((v, k) for k, v in fields.iteritems())
    def __getitem__(self, val):
        return self.__mapping[val]

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

Numbers = enum(ONE=1, TWO=2, THREE='three')
print Numbers.TWO
print Numbers[Numbers.ONE]
print Numbers[2]
print Numbers['three']
于 2012-06-21T22:43:55.343 に答える
2

Aaron Maenpaa によって提案された Java ライクな enum の実装に従って、次のような結果になりました。アイデアは、それを汎用的で解析可能にすることでした。

class Enum:
    #'''
    #Java like implementation for enums.
    #
    #Usage:
    #class Tool(Enum): name = 'Tool'
    #Tool.DRILL = Tool.register('drill')
    #Tool.HAMMER = Tool.register('hammer')
    #Tool.WRENCH = Tool.register('wrench')
    #'''

    name = 'Enum'    # Enum name
    _reg = dict([])   # Enum registered values

    @classmethod
    def register(cls, value):
        #'''
        #Registers a new value in this enum.
        #
        #@param value: New enum value.
        #
        #@return: New value wrapper instance.
        #'''
        inst = cls(value)
        cls._reg[value] = inst
        return inst

    @classmethod
    def parse(cls, value):
        #'''
        #Parses a value, returning the enum instance.
        #
        #@param value: Enum value.
        #
        #@return: Value corresp instance.        
        #'''
        return cls._reg.get(value)    

    def __init__(self, value):
        #'''
        #Constructor (only for internal use).
        #'''
        self.value = value

    def __str__(self):
        #'''
        #str() overload.
        #'''
        return self.value

    def __repr__(self):
        #'''
        #repr() overload.
        #'''
        return "<" + self.name + ": " + self.value + ">"
于 2010-03-05T20:24:21.943 に答える
2

なぜ列挙型は int でなければならないのですか? 残念ながら、Python 言語を変更せずにこれを生成する見栄えの良い構造を思いつかないので、文字列を使用します。

class Enumerator(object):
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        if self.name == other:
            return True
        return self is other

    def __ne__(self, other):
        if self.name != other:
            return False
        return self is other

    def __repr__(self):
        return 'Enumerator({0})'.format(self.name)

    def __str__(self):
        return self.name

class Enum(object):
    def __init__(self, *enumerators):
        for e in enumerators:
            setattr(self, e, Enumerator(e))
    def __getitem__(self, key):
        return getattr(self, key)

繰り返しますが、構成ファイルやその他のリモート入力のために、文字列に対して自然にテストできるようになった方が良いかもしれません。

例:

class Cow(object):
    State = Enum(
        'standing',
        'walking',
        'eating',
        'mooing',
        'sleeping',
        'dead',
        'dying'
    )
    state = State.standing

In [1]: from enum import Enum

In [2]: c = Cow()

In [3]: c2 = Cow()

In [4]: c.state, c2.state
Out[4]: (Enumerator(standing), Enumerator(standing))

In [5]: c.state == c2.state
Out[5]: True

In [6]: c.State.mooing
Out[6]: Enumerator(mooing)

In [7]: c.State['mooing']
Out[7]: Enumerator(mooing)

In [8]: c.state = Cow.State.dead

In [9]: c.state == c2.state
Out[9]: False

In [10]: c.state == Cow.State.dead
Out[10]: True

In [11]: c.state == 'dead'
Out[11]: True

In [12]: c.state == Cow.State['dead']
Out[11]: True
于 2010-05-07T02:05:03.740 に答える
2

二項演算子の左右の結合性を表すために、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カテゴリに含まれています。

于 2009-11-17T20:54:34.030 に答える
2

Python 2.7 と find_name()

これは、おそらく "reverse_mapping" より Pythonic でクリーンないくつかのヘルパー メソッドを使用した、選択したアイデアの読みやすい実装です。Python >= 2.7 が必要です。

以下のいくつかのコメントに対処するために、列挙型は、コードのスペルミスを防ぐのに非常に役立ちます。たとえば、ステートマシン、エラー分類子などです。

def Enum(*sequential, **named):
  """Generate a new enum type. Usage example:

  ErrorClass = Enum('STOP','GO')
  print ErrorClass.find_name(ErrorClass.STOP)
    = "STOP"
  print ErrorClass.find_val("STOP")
    = 0
  ErrorClass.FOO     # Raises AttributeError
  """
  enums = { v:k for k,v in enumerate(sequential) } if not named else named

  @classmethod
  def find_name(cls, val):
    result = [ k for k,v in cls.__dict__.iteritems() if v == val ]
    if not len(result):
        raise ValueError("Value %s not found in Enum" % val)
    return result[0]

  @classmethod
  def find_val(cls, n):
    return getattr(cls, n)

  enums['find_val'] = find_val
  enums['find_name'] = find_name
  return type('Enum', (), enums)
于 2013-04-08T18:58:52.113 に答える
1

私が通常使用するソリューションは、動的に作成されたクラスのインスタンスを取得するこの単純な関数です。

def enum(names):
    "Create a simple enumeration having similarities to C."
    return type('enum', (), dict(map(reversed, enumerate(
        names.replace(',', ' ').split())), __slots__=()))()

それを使用することは、参照したい名前を持つ文字列で関数を呼び出すのと同じくらい簡単です。

grade = enum('A B C D F')
state = enum('awake, sleeping, dead')

値は単なる整数であるため、必要に応じてそれを利用できます (C 言語と同様)。

>>> grade.A
0
>>> grade.B
1
>>> grade.F == 4
True
>>> state.dead == 2
True
于 2013-01-31T14:27:43.203 に答える
1
def enum( *names ):

    '''
    Makes enum.
    Usage:
        E = enum( 'YOUR', 'KEYS', 'HERE' )
        print( E.HERE )
    '''

    class Enum():
        pass
    for index, name in enumerate( names ):
        setattr( Enum, name, index )
    return Enum
于 2010-05-26T13:14:14.593 に答える