python - كيف يمكنني تمثيل "التعداد" في بايثون؟




python-3.x enums (20)

davidg توصي باستخدام dicts. سأذهب خطوة واحدة أخرى واستخدام مجموعات:

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

يمكنك الآن اختبار ما إذا كانت القيمة تطابق إحدى القيم في المجموعة كما يلي:

if m in months:

مثل dF ، رغم ذلك ، عادة ما أستخدم ثوابت السلسلة بدلاً من التعدادات.

أنا أساسا مطور C # ، ولكن أنا أعمل حاليا على مشروع في بيثون.

كيف يمكنني تمثيل ما يعادل التعداد في بايثون؟


أبقيها بسيطة:

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

أفضّل تعريف التعداد في بايثون كما يلي:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

إنه إثبات أكثر علة من استخدام أعداد صحيحة لأنك لست مضطرًا للقلق بشأن التأكد من أن الأعداد الصحيحة فريدة (على سبيل المثال ، إذا قلت أن الكلب = 1 و Cat = 1 ستكون مشدودًا).

إنها أكثر ثباتًا من استخدام السلاسل نظرًا لأنه لا داعي للقلق بشأن الأخطاء المطبعية (على سبيل المثال ، x == "catt" يفشل بصمت ، ولكن x == Animal.Catt هو استثناء وقت التشغيل).


أنا حقا أحب حل أليك توماس (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

يؤدي هذا إلى إنشاء تعداد يستند إلى نوع محدد. بالإضافة إلى إعطاء خاصية الوصول إلى السمة مثل الوظيفة السابقة ، فإنها تتصرف كما تتوقع من التعداد بالنسبة للأنواع. كما يرث الطبقة الأساسية.

على سبيل المثال ، التعدادات الصحيحة:

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

اقتراح ألكساندرو لاستخدام الثوابت الطبقية للتعدادات يعمل بشكل جيد.

أود أيضًا أن أضيف قاموسًا لكل مجموعة من الثوابت للبحث عن تمثيل سلسلة يمكن أن يقرأه الإنسان.

هذا يخدم غرضين: أ) يوفر طريقة بسيطة لطباعة التعداد الخاص بك بشكل جيد و 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())

المعيار الجديد في 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

توفر حزمة التعداد من PyPI تنفيذ قوي PyPI . ذكر إجابة سابقة 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'

في 10-10 ، وافق غيدو على قبول PEP 435 في مكتبة Python 3.4 القياسية. وهذا يعني أن بيثون قد أيدت في النهاية الدعم في التعدادات!

يوجد backport متاح لـ 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

لمزيد من المعلومات ، يرجى الرجوع إلى الاقتراح . من المحتمل أن تتبع الوثائق الرسمية قريبًا.


لا تملك بايثون بنية مكافئة enum ، كما أن هناك إجابات أخرى لديها أفكار لتنفيذ تطبيقك الخاص (قد تكون مهتمًا أيضًا here في كتاب Python).

ومع ذلك ، في الحالات التي يتم فيها استدعاء enum في C ، عادة ما ينتهي بي المطاف باستخدام سلاسل بسيطة فقط : نظرًا للطريقة التي يتم بها تنفيذ الكائنات / السمات ، (C) تم تحسين Python للعمل بسرعة كبيرة مع سلاسل قصيرة على أي حال ، لذلك هناك لن يكون حقا أي فائدة الأداء لاستخدام الأعداد الصحيحة. للحماية من الأخطاء المطبعية / القيم غير الصحيحة ، يمكنك إدراج عمليات التحقق في أماكن محددة.

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

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

(أحد العيوب مقارنة باستخدام الطبقة هو أنك تفقد فائدة الإكمال التلقائي)


لذا ، أنا أوافق. دعونا لا نفرض سلامة النوع في بيثون ، لكني أود أن أحمي نفسي من الأخطاء السخيفة. إذن ما رأيك في هذا؟

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]

ما أستخدمه:

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 و twuples لاستخدامها كخيارات في نماذج Django.


من Python 3.4 سيكون هناك دعم رسمي للتعدادات. يمكنك العثور على وثائق وأمثلة هنا في صفحة وثائق Python 3.4 .

يتم إنشاء التعدادات باستخدام بنية الفئة ، مما يجعلها سهلة القراءة والكتابة. يتم وصف طريقة إنشاء بديلة في API الوظيفية. لتعريف تعداد ، فئة فرعية التعداد كما يلي:

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 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',
))

هذا هو أفضل واحد رأيته: "التوعية من الدرجة الأولى في بايثون"

يعطيك فئة ، والصف يحتوي على جميع التعدادات. يمكن مقارنة التعدادات ببعضها البعض ، ولكن ليس لها أي قيمة معينة ؛ لا يمكنك استخدامها كقيمة صحيحة. (قاومت هذا في البداية لأنني اعتدت على التعدادات C ، والتي هي قيم صحيحة. ولكن إذا كنت لا تستطيع استخدامها كعدد صحيح ، لا يمكنك استخدامه كعدد صحيح عن طريق الخطأ ، لذا بشكل عام أعتقد أنه الفوز .) كل تعداد قيمة فريدة. يمكنك طباعة التعدادات ، يمكنك التكرار عليها ، يمكنك اختبار أن قيمة التعداد هي "في" التعداد. انها جميلة جدا والبقعة.

تحرير (cfi): الرابط أعلاه ليس متوافقًا مع Python 3. هنا مرفأ enum.py الخاص بي إلى Python 3:

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)

هنا تنفيذ واحد:

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

هنا هو استخدامه:

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

print(Animals.DOG)

هنا هو البديل على حل أليك توماس :

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

Here's an approach with some different characteristics I find valuable:

  • allows > and < comparison based on order in enum, not lexical order
  • can address item by name, property or index: xa, x['a'] or x[0]
  • supports slicing operations like [:] or [-1]

and most importantly prevents comparisons between enums of different types !

Based closely on http://code.activestate.com/recipes/413486-first-class-enums-in-python .

Many doctests included here to illustrate what's different about this approach.

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

I had need of some symbolic constants in pyparsing to represent left and right associativity of binary operators. I used class constants like this:

# 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()

Now when client code wants to use these constants, they can import the entire enum using:

import opAssoc from pyparsing

تُعد التعدادات فريدة من نوعها ، ويمكن اختبارها باستخدام "is" بدلاً من "==" ، ولا تأخذ بصمة كبيرة في التعليمة البرمجية الخاصة بي لمفهوم بسيط ، ويتم استيرادها بسهولة إلى رمز العميل. لا يدعمون أي سلوك str () YAGNI ، ولكن حتى الآن في فئة YAGNI .


تمت إضافة التعداد إلى Python 3.4 كما هو موضح في PEP 435 . كما تم إعادته إلى 3.3 و 3.2 و 3.1 و 2.7 و 2.6 و 2.5 و 2.4 على pypi.

بالنسبة إلى تقنيات التعداد الأكثر تقدمًا ، حاول استخدام مكتبة 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

في الإصدارات السابقة ، إحدى الطرق لإنجاز التعدادات هي:

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'

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