Enum - konstantite hulk

Enum ehk konstantide loend on hulk muutujatest, mille igale liikmele vastab unikaalne väärtus. Oma olemuselt on Enumid nagu globaalsed muutujad, kuid neid on võimalik grupeerida ning määrata kindel tüüp. Erinevalt tavalistest klassidest, on enum tüüpi klassidel __repr__ funktsioon juba olemas, seda eraldi kirjutama ei pea.

Enum koostamiseks on kõigepealt vaja importida nimeruumi moodul enum, sealt klass Enum ning seejärel luua klass, mis pärineb Enumist. Enum muutujate väärtused võivad ükskõik millised konstantsed muutumatud väärtused olla (int, str, tuple jne.). Kui muutuja väärtus pole oluline, võib kasutada auto() funktsiooni, mis genereerib automaatselt unikaalse väärtuse muutujale.

from enum import Enum, auto # import auto if values are to be generated automatically

class ExampleEnum(Enum):
    FOO = 0
    BAR = 1
    STRING_EXAMPLE = "FooBar"
    TUPLE_EXAMPLE = ("Foo", "Bar")
    AUTO_EXAMPLE = auto() # In practice, you would want to approach with caution to mixing manually and automatically generated values
  • Kuna reeglina hoiustatakse enumites konstantseid väärtuseid, siis muutujate nimed kirjutatakse suurte tähtedega.

Igal enum muutujal on kaks atribuuti: name ja value name - tagastab muutuja nime. value - tagastab muutuja väärtuse.

choosen_enum = ExampleEnum.FOO
print(choosen_enum.name) # => FOO
print(choosen_enum.value) # => 0

Enumit kutsutakse välja tema muutjua väärtuste või nime järgi. Kui kumbagi ei eksisteeri, tagastatakse kas KeyError või ValueError:

print(ExampleEnum(1)) # => ExampleEnum.BAR (Call by value)
print(ExampleEnum["STRING_EXAMPLE"]) # => ExampleEnum.STRING_EXAMPLE (Call by key)
print(ExampleEnum(("Foo", "Bar"))) # => ExampleEnum.TUPLE_EXAMPLE
print(ExampleEnum["aaaaa"]) # => KeyError: 'aaaaa'
print(ExampleEnum(2)) # => ValueError: 2 is not a valid ExampleEnum
  • Kahe või enama sama nimelise muutuja koostamine lõppeb TypeErroriga, kuid väärtused võivad samad olla - seljuhul tagastatakse otsingul suurema kaaluga (kõige varasemalt koostatud) muutuja.

class ExampleEnum(Enum):
    FOO = 0
    BAR = 1
    FOO_ALIAS = 0

print(ExampleEnum(0)) # => ExampleEnum.FOO
print(ExampleEnum.FOO) # => ExampleEnum.FOO
print(ExampleEnum.FOO_ALIAS) # => ExampleEnum.FOO

Eelnevalt toodud käitumist on võimalik vältida kasutades @unique dekoraatorit:

from enum import Enum, unique
@unique
class ExampleEnum(Enum):
    FOO = 0
    BAR = 1
    FOO_ALIAS = 0

# After running the script the following error will appear:
# ValueError: duplicate values found in <enum 'ExampleEnum'>: FOO_ALIAS -> FOO

Puuduva väärtuse puhul on võimalik errori asemel tagastada __missing__ klassimeetodi kaudu defineeritud väärtus:

class ExampleEnum(Enum):
    FOO = 0
    BAR = 1
    NONE = None
    @classmethod
    def __missing__(cls, value):
        return ExampleEnum.NONE

print(ExampleEnum(1)) # => ExampleEnum.BAR
print(ExampleEnum(2)) # => ExampleEnum.NONE
  • return None ei takista ValueError teket, kuna klassimeetodis tagastatav väärtus peab olema antud enum tüüpi.

Dekoraatori @classmethod ei piirdu ainult default meetodite koostamiseks, kui vajadus tekib, on võimalik ise ka meetodeid antud dekoraatoriga kirjutada ning neid välja kutsuda (nt: ExampleEnum.custom_method(input_args))

Enum puhul on võimalik ainult läbi viia tõeväärsus võrdlusi:

print(ExampleEnum.FOO is ExampleEnum.FOO) # => True
print(ExampleEnum.FOO is ExampleEnum.BAR) # => False
print(ExampleEnum.FOO is not ExampleEnum.BAR) # => True

print(ExampleEnum.FOO == ExampleEnum.FOO) # => True
print(ExampleEnum.FOO == ExampleEnum.BAR) # => False
print(ExampleEnum.FOO != ExampleEnum.BAR) # => True

print(ExampleEnum.FOO > ExampleEnum.BAR) # => TypeError

Enumeid on võimalik läbi loendada:

class ExampleEnum(Enum):
    FOO = 0
    BAR = 1
    FOO_ALIAS = 0

print(list(ExampleEnum)) # => [<ExampleEnum.FOO: 0>, <ExampleEnum.BAR: 1>]
  • Läbi loendades jäetakse duplikaat väärtustega muutujad loendamata


Enumeid saab praktikas kasutada näiteks mänguarenduses mängu staatuse kirjeldamisel. Olgu meie mängus eri etapid (ootamine, alustamine, mängimine, lõpetamine). Nende tähistamiseks koostame järgneva enumi:

class GameState(Enum):
    WAITING = 0
    STARTING = 1
    PLAYING = 2
    ENDING = 3

Oletame, et mingi muu osa koodist omistab praeguse mängustaatuse automaatselt

self.game_state = GameState.PLAYING

Järgnevalt on võimalik kontrollida, mis seisus mäng praegu on ning vastavalt sellele teha järgmisi asju. Näiteks mängija mängu lisamine, kui mäng pole veel alanud:

def add_player_to_game(self, player):
    """Adds player to the game, if game hasn't started yet."""
    if self.game_state == GameState.WAITING or self.game_state == GameState.STARTING:
        self.players.append(player)

Eelnev on üks tüüpilistest viisidest, kuidas Enumeid kasutatakse ehk konstantide koostamine ja nende põhjal millegi tegemine.


Edasi lugemiseks: https://docs.python.org/3/library/enum.html