Pertanyaan Bagaimana saya bisa mewakili 'Enum' dengan Python?


Saya terutama pengembang C #, tapi saat ini saya sedang mengerjakan proyek dengan Python.

Bagaimana saya bisa mewakili setara dengan Enum dengan Python?


1146


asal


Jawaban:


Enums telah ditambahkan ke Python 3.4 seperti yang dijelaskan di PEP 435. Itu juga di-backport ke 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, dan 2.4 di pypi.

Untuk teknik Enum yang lebih canggih, coba perpustakaan aenum (2.7, 3.3+, penulis yang sama dengan enum34. Kode tidak kompatibel sempurna antara py2 dan py3, mis. kamu akan membutuhkan __order__ di python 2).

  • Menggunakan enum34, lakukan $ pip install enum34
  • Menggunakan aenum, lakukan $ pip install aenum

Menginstal enum (tidak ada nomor) akan menginstal versi yang benar-benar berbeda dan tidak kompatibel.


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)

atau dengan kata lain:

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

Dalam versi sebelumnya, salah satu cara untuk menyelesaikan enums adalah:

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

yang digunakan seperti ini:

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

Anda juga dapat dengan mudah mendukung enumerasi otomatis dengan sesuatu seperti ini:

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

dan digunakan seperti ini:

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

Dukungan untuk mengubah nilai kembali ke nama dapat ditambahkan dengan cara ini:

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)

Ini menimpa apa pun dengan nama itu, tetapi ini berguna untuk menerjemahkan enum Anda dalam output. Ini akan membuang KeyError jika pemetaan terbalik tidak ada. Dengan contoh pertama:

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

2320



Sebelum PEP 435, Python tidak memiliki padanan tetapi Anda bisa mengimplementasikannya sendiri.

Sendiri, saya suka menjaganya tetap sederhana (saya telah melihat beberapa contoh yang sangat rumit di internet), sesuatu seperti ini ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Dalam Python 3.4 (PEP 435), Anda bisa membuatnya Enum kelas dasar. Ini memberi Anda sedikit fungsi tambahan, yang dijelaskan dalam PEP. Misalnya, anggota enum berbeda dari bilangan bulat, dan mereka terdiri dari a name dan a value.

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

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

print(Animal.DOG.value)
# 1

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

Jika Anda tidak ingin mengetikkan nilai, gunakan pintasan berikut:

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

Enum implementasi dapat dikonversi ke daftar dan dapat diulangi. Urutan anggotanya adalah perintah deklarasi dan tidak ada hubungannya dengan nilai-nilai mereka. Sebagai contoh:

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

721



Berikut ini salah satu penerapannya:

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

Berikut ini penggunaannya:

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

print(Animals.DOG)

298



Jika Anda memerlukan nilai numerik, berikut cara tercepat:

dog, cat, rabbit = range(3)

Dalam Python 3.x Anda juga dapat menambahkan placeholder berbintang di bagian akhir, yang akan menyerap semua nilai yang tersisa dari jangkauan jika Anda tidak keberatan membuang-buang memori dan tidak dapat menghitung:

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

183



Solusi terbaik untuk Anda akan bergantung pada apa yang Anda butuhkan dari Anda palsu  enum.

Enum sederhana:

Jika Anda membutuhkan enum hanya sebagai daftar nama-nama mengidentifikasi yang berbeda item, solusi oleh Mark Harrison (di atas) luar biasa:

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

Menggunakan sebuah range juga memungkinkan Anda mengaturnya nilai awal:

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

Selain di atas, jika Anda juga mengharuskan barang milik a wadah semacam itu, lalu menyematkan mereka di kelas:

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

Untuk menggunakan item enum, Anda sekarang harus menggunakan nama penampung dan nama item:

stype = Stationery.Pen

Enum kompleks:

Untuk daftar panjang enum atau penggunaan enum yang lebih rumit, solusi ini tidak akan mencukupi. Anda bisa melihat resep oleh Will Ware untuk Simulasi Enumerations dengan Python diterbitkan dalam Python Cookbook. Versi online yang tersedia sini.

Info lebih lanjut:

PEP 354: Enumerations dengan Python memiliki rincian menarik dari sebuah proposal untuk enum dengan Python dan mengapa itu ditolak.


119



Pola-pola enum typesafe yang digunakan di Java pra-JDK 5 memiliki a jumlah keuntungan. Sama seperti jawaban Alexandru, Anda membuat a bidang kelas dan kelas adalah nilai enum; Namun, enum nilai adalah contoh kelas daripada bilangan bulat kecil. Ini mempunyai keuntungan bahwa nilai enum Anda tidak secara tidak sengaja membandingkan sama ke bilangan bulat kecil, Anda dapat mengontrol bagaimana mereka dicetak, tambahkan sewenang-wenang metode jika itu berguna dan membuat pernyataan menggunakan isstst:

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

Baru baru ini utas python-dev menunjukkan ada beberapa perpustakaan enum di alam liar, termasuk:


76



Kelas Enum bisa menjadi satu-liner.

class Enum(tuple): __getattr__ = tuple.index

Cara menggunakannya (pencarian maju dan mundur, kunci, nilai, item, dll.)

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

56



Python tidak memiliki built-in yang setara dengan enum, dan jawaban lain memiliki ide untuk menerapkan Anda sendiri (Anda mungkin juga tertarik dengan di atas versi teratas dalam buku resep Python).

Namun, dalam situasi di mana suatu enum akan dipanggil di C, saya biasanya berakhir hanya menggunakan string sederhana: karena cara objek / atribut diimplementasikan, (C) Python dioptimalkan untuk bekerja sangat cepat dengan string pendek pula, sehingga tidak akan ada manfaat kinerja untuk menggunakan bilangan bulat. Untuk menjaga terhadap kesalahan ketik / nilai tidak valid Anda dapat memasukkan cek di tempat-tempat yang dipilih.

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

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

(Satu kerugian dibandingkan menggunakan kelas adalah Anda kehilangan manfaat pelengkapan otomatis)


44



Jadi saya setuju. Jangan memaksakan jenis keamanan dengan Python, tetapi saya ingin melindungi diri dari kesalahan konyol. Jadi apa yang kita pikirkan tentang ini?

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

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

Itu membuat saya dari tabrakan nilai dalam mendefinisikan enums saya.

>>> Animal.Cat
2

Ada keuntungan lain yang berguna: pencarian balik yang sangat cepat:

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

44



Pada 2013-05-10, Guido setuju untuk menerima PEP 435 ke dalam pustaka standar Python 3.4. Ini berarti bahwa Python akhirnya telah membangun dukungan untuk enumerasi!

Ada backport yang tersedia untuk Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, dan 2.4. Ada di Pypi sebagai enum34.

Pernyataan:

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

Perwakilan:

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

Perulangan:

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

Akses terprogram:

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

Untuk informasi lebih lanjut, lihat proposal. Dokumentasi resmi mungkin akan segera menyusul.


30



Saya lebih suka mendefinisikan enum dengan Python seperti:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

Ini lebih bug-bukti daripada menggunakan bilangan bulat karena Anda tidak perlu khawatir tentang memastikan bahwa bilangan bulat adalah unik (misalnya jika Anda mengatakan Dog = 1 dan Cat = 1 Anda akan kacau).

Ini lebih tahan bug daripada menggunakan string karena Anda tidak perlu khawatir tentang kesalahan ketik (mis. x == "catt" gagal dalam diam, tetapi x == Animal.Catt adalah pengecualian runtime).


29