# 8.13. `enum` — Énumerations

Nouveau dans la version 3.4.

Code source : Lib/enum.py

Une énumération est un ensemble de noms symboliques, appelés membres, liés à des valeurs constantes et uniques. Au sein d’une énumération, les membres peuvent être comparés entre eux et il est possible d’itérer sur l’énumération elle-même.

## 8.13.1. Contenu du module

Ce module définit quatre classes d’énumération qui permettent de définir des ensembles uniques de noms et de valeurs : `Enum`, `IntEnum`, `Flag` et `IntFlag`. Il fournit également un décorateur, `unique()`, ainsi qu’une classe utilitaire, `auto`.

class `enum.``Enum`

  Classe de base pour créer une énumération de constantes. Voir la section API par fonction pour une syntaxe alternative de construction.

class `enum.``IntEnum`

  Classe de base pour créer une énumération de constantes qui sont également des sous-classes de `int`.

class `enum.``IntFlag`

  Classe de base pour créer une énumération de constantes pouvant être combinées avec des opérateurs de comparaison bit-à-bit, sans perdre leur qualité de `IntFlag`. Les membres de `IntFlag` sont aussi des sous-classes de `int`.

class `enum.``Flag`

  Classe de base pour créer une énumération de constantes pouvant être combinées avec des opérateurs de comparaison bit-à-bit, sans perdre leur qualité de `Flag`.

`enum.``unique`()

  Décorateur de classe qui garantit qu’une valeur ne puisse être associée qu’à un seul nom.

class `enum.``auto`

  Les instances de cette classe remplacent les membres d’une Enum par une valeur appropriée.

Nouveau dans la version 3.6: `Flag`, `IntFlag`, `auto`

## 8.13.2. Création d’une Enum

Une énumération est créée comme une `class`, ce qui la rend facile à lire et à écrire. Une autre méthode de création est décrite dans API par fonction. Pour définir une énumération, il faut hériter de `Enum` de la manière suivante :

    >>> from enum import Enum
    >>> class Color(Enum):
    ...     RED = 1
    ...     GREEN = 2
    ...     BLUE = 3
    ...

Note

Valeurs des membres d’une Enum

La valeur d’un membre peut être de n’importe quel type : `int`, `str`, etc. Si la valeur exacte n’a pas d’importance, utilisez des instances de `auto` et une valeur appropriée sera choisie pour vous. Soyez vigilant si vous mélangez `auto` avec d’autres valeurs.

Note

Nomenclature

- La classe `Color` est une énumération (ou un enum).

- Les attributs `Color.RED`, `Color.GREEN`, etc., sont les membres de l’énumération (ou les membres de l’enum) et sont fonctionnellement des constantes.

- Les membres de l’enum ont chacun un nom et une valeur ; le nom de `Color.RED` est `RED`, la valeur de `Color.BLUE` est `3`, etc.

Note

Même si on utilise la syntaxe en `class` pour créer des énumérations, les Enums ne sont pas des vraies classes Python. Voir En quoi les Enums sont différentes ? pour plus de détails.

Les membres d’une énumération ont une représentation en chaîne de caractères compréhensible par un humain :

    >>> print(Color.RED)
    Color.RED

… tandis que leur `repr` contient plus d’informations :

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

Le type d’un membre est l’énumération auquel ce membre appartient :

    >>> type(Color.RED)
    <enum 'Color'>
    >>> isinstance(Color.GREEN, Color)
    True
    >>>

Les membres ont également un attribut qui contient leur nom :

    >>> print(Color.RED.name)
    RED

Les énumérations sont itérables, l’ordre d’itération est celui dans lequel les membres sont déclarés :

    >>> class Shake(Enum):
    ...     VANILLA = 7
    ...     CHOCOLATE = 4
    ...     COOKIES = 9
    ...     MINT = 3
    ...
    >>> for shake in Shake:
    ...     print(shake)
    ...
    Shake.VANILLA
    Shake.CHOCOLATE
    Shake.COOKIES
    Shake.MINT

Les membres d’une énumération sont hachables, ils peuvent ainsi être utilisés dans des dictionnaires ou des ensembles :

    >>> apples = {}
    >>> apples[Color.RED] = 'red delicious'
    >>> apples[Color.GREEN] = 'granny smith'
    >>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
    True

## 8.13.3. Accès dynamique aux membres et à leurs attributs

Il est parfois utile de pouvoir accéder dynamiquement aux membres d’une énumération (p. ex. dans des situations où il ne suffit pas d’utiliser `Color.RED` car la couleur précise n’est pas connue à l’écriture du programme). `Enum` permet de tels accès :

    >>> Color(1)
    <Color.RED: 1>
    >>> Color(3)
    <Color.BLUE: 3>

Pour accéder aux membres par leur nom, utilisez l’accès par indexation :

    >>> Color['RED']
    <Color.RED: 1>
    >>> Color['GREEN']
    <Color.GREEN: 2>

Pour obtenir l’attribut `name` ou `value` d’un membre :

    >>> member = Color.RED
    >>> member.name
    'RED'
    >>> member.value
    1

## 8.13.4. Duplication de membres et de valeurs

Il n’est pas possible d’avoir deux membres du même nom dans un enum :

    >>> class Shape(Enum):
    ...     SQUARE = 2
    ...     SQUARE = 3
    ...
    Traceback (most recent call last):
    ...
    TypeError: Attempted to reuse key: 'SQUARE'

Cependant deux membres peuvent avoir la même valeur. Si deux membres A et B ont la même valeur (et que A est défini en premier), B sera un alias de A. Un accès par valeur avec la valeur commune à A et B renverra A. Un accès à B par nom renverra aussi A :

    >>> class Shape(Enum):
    ...     SQUARE = 2
    ...     DIAMOND = 1
    ...     CIRCLE = 3
    ...     ALIAS_FOR_SQUARE = 2
    ...
    >>> Shape.SQUARE
    <Shape.SQUARE: 2>
    >>> Shape.ALIAS_FOR_SQUARE
    <Shape.SQUARE: 2>
    >>> Shape(2)
    <Shape.SQUARE: 2>

Note

Il est interdit de créer un membre avec le même nom qu’un attribut déjà défini (un autre membre, une méthode, etc.) ou de créer un attribut avec le nom d’un membre.

## 8.13.5. Coercition d’unicité des valeurs d’une énumération

Par défaut, les énumérations autorisent les alias de nom pour une même valeur. Quand ce comportement n’est pas désiré, il faut utiliser le décorateur suivant pour s’assurer que chaque valeur n’est utilisée qu’une seule fois au sein de l’énumération :

`@``enum.``unique`

Un décorateur de `class` spécifique aux énumérations. Il examine l’attribut `__members__` d’une énumération et recherche des alias ; s’il en trouve, l’exception `ValueError` est levée avec des détails :

    >>> from enum import Enum, unique
    >>> @unique
    ... class Mistake(Enum):
    ...     ONE = 1
    ...     TWO = 2
    ...     THREE = 3
    ...     FOUR = 3
    ...
    Traceback (most recent call last):
    ...
    ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

## 8.13.6. Valeurs automatiques

Si la valeur exacte n’a pas d’importance, vous pouvez utiliser `auto` :

    >>> from enum import Enum, auto
    >>> class Color(Enum):
    ...     RED = auto()
    ...     BLUE = auto()
    ...     GREEN = auto()
    ...
    >>> list(Color)
    [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Les valeurs sont déterminées par `_generate_next_value_()`, qui peut être redéfinie :

    >>> class AutoName(Enum):
    ...     def _generate_next_value_(name, start, count, last_values):
    ...         return name
    ...
    >>> class Ordinal(AutoName):
    ...     NORTH = auto()
    ...     SOUTH = auto()
    ...     EAST = auto()
    ...     WEST = auto()
    ...
    >>> list(Ordinal)
    [<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

Note

La méthode par défaut `_generate_next_value_()` doit fournir le `int` suivant de la séquence en fonction du dernier `int` fourni, mais la séquence générée dépend de l’implémentation Python.

## 8.13.7. Itération

Itérer sur les membres d’une énumération ne parcourt pas les alias :

    >>> list(Shape)
    [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]

L’attribut spécial `__members__` est un dictionnaire ordonné qui fait correspondre les noms aux membres. Il inclut tous les noms définis dans l’énumération, alias compris :

    >>> for name, member in Shape.__members__.items():
    ...     name, member
    ...
    ('SQUARE', <Shape.SQUARE: 2>)
    ('DIAMOND', <Shape.DIAMOND: 1>)
    ('CIRCLE', <Shape.CIRCLE: 3>)
    ('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)

L’attribut `__members__` peut servir à accéder dynamiquement aux membres de l’énumération. Par exemple, pour trouver tous les alias :

    >>> [name for name, member in Shape.__members__.items() if member.name != name]
    ['ALIAS_FOR_SQUARE']

## 8.13.8. Comparaisons

Les membres d’une énumération sont comparés par identité :

    >>> Color.RED is Color.RED
    True
    >>> Color.RED is Color.BLUE
    False
    >>> Color.RED is not Color.BLUE
    True

Les comparaisons d’ordre entre les valeurs d’une énumération n’existent pas ; les membres d’un enum ne sont pas des entiers (voir cependant IntEnum ci-dessous) :

    >>> Color.RED < Color.BLUE
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: '<' not supported between instances of 'Color' and 'Color'

A contrario, les comparaisons d’égalité existent :

    >>> Color.BLUE == Color.RED
    False
    >>> Color.BLUE != Color.RED
    True
    >>> Color.BLUE == Color.BLUE
    True

Les comparaisons avec des valeurs ne provenant pas d’énumérations sont toujours fausses (ici encore, `IntEnum` a été conçue pour fonctionner différemment, voir ci-dessous) :

    >>> Color.BLUE == 2
    False

## 8.13.9. Membres et attributs autorisés dans une énumération

Les exemples précédents utilisent des entiers pour énumérer les valeurs. C’est un choix concis et pratique (et implémenté par défaut dans l”API par fonction), mais ce n’est pas une obligation. Dans la majorité des cas, il importe peu de connaître la valeur réelle d’une énumération. Il est toutefois possible de donner une valeur arbitraire aux énumérations, si cette valeur est vraiment significative.

Les énumérations sont des classes Python et peuvent donc avoir des méthodes et des méthodes spéciales. L’énumération suivante :

    >>> class Mood(Enum):
    ...     FUNKY = 1
    ...     HAPPY = 3
    ...
    ...     def describe(self):
    ...         # self is the member here
    ...         return self.name, self.value
    ...
    ...     def __str__(self):
    ...         return 'my custom str! {0}'.format(self.value)
    ...
    ...     @classmethod
    ...     def favorite_mood(cls):
    ...         # cls here is the enumeration
    ...         return cls.HAPPY
    ...

amène :

    >>> Mood.favorite_mood()
    <Mood.HAPPY: 3>
    >>> Mood.HAPPY.describe()
    ('HAPPY', 3)
    >>> str(Mood.FUNKY)
    'my custom str! 1'

The rules for what is allowed are as follows: names that start and end with a single underscore are reserved by enum and cannot be used; all other attributes defined within an enumeration will become members of this enumeration, with the exception of special methods (`__str__()`, `__add__()`, etc.) and descriptors (methods are also descriptors).

Remarque : si l’énumération définit `__new__()` ou `__init__()`, alors la (ou les) valeur affectée au membre sera passée à ces méthodes. Voir l’exemple de Planet.

## 8.13.10. Restricted subclassing of enumerations

Subclassing an enumeration is allowed only if the enumeration does not define any members. So this is forbidden:

    >>> class MoreColor(Color):
    ...     PINK = 17
    ...
    Traceback (most recent call last):
    ...
    TypeError: Cannot extend enumerations

Mais celui-ci est correct :

    >>> class Foo(Enum):
    ...     def some_behavior(self):
    ...         pass
    ...
    >>> class Bar(Foo):
    ...     HAPPY = 1
    ...     SAD = 2
    ...

Autoriser l’héritage d”enums définissant des membres violerait des invariants sur les types et les instances. D’un autre côté, il est logique d’autoriser un groupe d’énumérations à partager un comportement commun (voir par exemple OrderedEnum).

## 8.13.11. Sérialisation

Les énumérations peuvent être sérialisées et déserialisées :

    >>> from test.test_enum import Fruit
    >>> from pickle import dumps, loads
    >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
    True

Les restrictions habituelles de sérialisation s’appliquent : les enums à sérialiser doivent être déclarés dans l’espace de nom de haut niveau du module car la déserialisation nécessite que ces enums puissent être importés depuis ce module.

Note

Depuis la version 4 du protocole de pickle, il est possible de sérialiser facilement des enums imbriqués dans d’autres classes.

Redéfinir la méthode `__reduce_ex__()` permet de modifier la sérialisation ou la dé-sérialisation des membres d’une énumération.

## 8.13.12. API par fonction

La `Enum` est appelable et implémente l’API par fonction suivante :

    >>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
    >>> Animal
    <enum 'Animal'>
    >>> Animal.ANT
    <Animal.ANT: 1>
    >>> Animal.ANT.value
    1
    >>> list(Animal)
    [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

La sémantique de cette API est similaire à `namedtuple`. Le premier argument de l’appel à `Enum` est le nom de l’énumération.

Le second argument est la source des noms des membres de l’énumération. Il peut être une chaîne de caractères contenant les noms séparés par des espaces, une séquence de noms, une séquence de couples clé / valeur ou un dictionnaire (p. ex. un dict) de valeurs indexées par des noms. Les deux dernières options permettent d’affecter des valeurs arbitraires aux énumérations ; les autres affectent automatiquement des entiers en commençant par 1 (le paramètre `start` permet de changer la valeur de départ). Ceci renvoie une nouvelle classe dérivée de `Enum`. En d’autres termes, la déclaration de `Animal` ci-dessus équivaut à :

    >>> class Animal(Enum):
    ...     ANT = 1
    ...     BEE = 2
    ...     CAT = 3
    ...     DOG = 4
    ...

La valeur de départ par défaut est `1` et non `0` car `0` au sens booléen vaut `False` alors que tous les membres d’une enum valent `True`.

La sérialisation d’énumérations créées avec l’API en fonction peut être source de problèmes, car celle-ci repose sur des détails d’implémentation de l’affichage de la pile d’appel pour tenter de déterminer dans quel module l’énumération est créée (p. ex. elle échouera avec les fonctions utilitaires provenant d’un module séparé et peut ne pas fonctionner avec IronPython ou Jython). La solution consiste à préciser explicitement le nom du module comme ceci :

    >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)

Avertissement

Si `module` n’est pas fourni et que Enum ne peut pas le deviner, les nouveaux membres de l’Enum ne seront pas déserialisables ; pour garder les erreurs au plus près de leur origine, la sérialisation sera désactivée.

Le nouveau protocole version 4 de pickle se base lui aussi, dans certains cas, sur le fait que `__qualname__` pointe sur l’endroit où pickle peut trouver la classe. Par exemple, si la classe était disponible depuis la classe SomeData dans l’espace de nom de plus haut niveau :

    >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')

La signature complète est la suivante :

    Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)

value

  Le nom de la la nouvelle classe Enum.

names

  Les membres de l’énumération. Une chaîne de caractères séparés par des espaces ou des virgules (la valeur de départ est fixée à 1, sauf si spécifiée autrement) :

      'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'

  ou un itérateur sur les noms :

      ['RED', 'GREEN', 'BLUE']

  ou un itérateur sur les tuples (nom, valeur) :

      [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]

  ou une correspondance :

      {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}

module

  nom du module dans lequel la classe Enum se trouve.

qualname

  localisation de la nouvelle classe Enum dans le module.

type

  le type à mélanger dans la nouvelle classe Enum.

start

  index de départ si uniquement des noms sont passés.

Modifié dans la version 3.5: Ajout du paramètre start.

## 8.13.13. Énumérations dérivées

### 8.13.13.1. IntEnum

La première version dérivée de `Enum` qui existe est aussi une sous-classe de `int`. Les membres de `IntEnum` peuvent être comparés à des entiers et, par extension, les comparaisons entre des énumérations entières de type différent sont possibles :

    >>> from enum import IntEnum
    >>> class Shape(IntEnum):
    ...     CIRCLE = 1
    ...     SQUARE = 2
    ...
    >>> class Request(IntEnum):
    ...     POST = 1
    ...     GET = 2
    ...
    >>> Shape == 1
    False
    >>> Shape.CIRCLE == 1
    True
    >>> Shape.CIRCLE == Request.POST
    True

Elles ne peuvent cependant toujours pas être comparées à des énumérations standards de `Enum` :

    >>> class Shape(IntEnum):
    ...     CIRCLE = 1
    ...     SQUARE = 2
    ...
    >>> class Color(Enum):
    ...     RED = 1
    ...     GREEN = 2
    ...
    >>> Shape.CIRCLE == Color.RED
    False

Les valeurs de `IntEnum` se comportent comme des entiers, comme on pouvait s’y attendre :

    >>> int(Shape.CIRCLE)
    1
    >>> ['a', 'b', 'c'][Shape.CIRCLE]
    'b'
    >>> [i for i in range(Shape.SQUARE)]
    [0, 1]

### 8.13.13.2. IntFlag

La version dérivée suivante de `Enum` est `IntFlag`. Elle est aussi basée sur `int`, à la différence près que les membres de `IntFlag` peuvent être combinés en utilisant les opérateurs bit-à-bit (&, |, ^, ~) et que le résultat reste un membre de `IntFlag`. Cependant, comme le nom l’indique, les membres d’une classe `IntFlag` héritent aussi de `int` et peuvent être utilisés là où un `int` est utilisé. Toute opération sur un membre d’une classe `IntFlag`, autre qu’un opérateur bit-à-bit lui fait perdre sa qualité de `IntFlag`.

Nouveau dans la version 3.6.

Exemple d’une classe `IntFlag` :

    >>> from enum import IntFlag
    >>> class Perm(IntFlag):
    ...     R = 4
    ...     W = 2
    ...     X = 1
    ...
    >>> Perm.R | Perm.W
    <Perm.R|W: 6>
    >>> Perm.R + Perm.W
    6
    >>> RW = Perm.R | Perm.W
    >>> Perm.R in RW
    True

Il est aussi possible de nommer les combinaisons :

    >>> class Perm(IntFlag):
    ...     R = 4
    ...     W = 2
    ...     X = 1
    ...     RWX = 7
    >>> Perm.RWX
    <Perm.RWX: 7>
    >>> ~Perm.RWX
    <Perm.-8: -8>

Une autre différence importante entre `IntFlag` et `Enum` est que, si aucune option n’est activée (la valeur vaut 0), son évaluation booléenne est `False` :

    >>> Perm.R & Perm.X
    <Perm.0: 0>
    >>> bool(Perm.R & Perm.X)
    False

Comme les membres d’une classe `IntFlag` héritent aussi de `int`, ils peuvent être combinés avec eux :

    >>> Perm.X | 8
    <Perm.8|X: 9>

### 8.13.13.3. Option

La dernière version dérivée est la classe `Flag`. Comme `IntFlag`, les membres d’une classe `Flag` peuvent être combinés en utilisant les opérateurs de comparaison bit-à-bit. Cependant, à la différence de `IntFlag`, ils ne peuvent ni être combinés, ni être comparés avec une autre énumération `Flag`, ni avec `int`. Bien qu’il soit possible de définir directement les valeurs, il est recommandé d’utiliser `auto` comme valeur et de laisser `Flag` choisir une valeur appropriée.

Nouveau dans la version 3.6.

Comme avec `IntFlag`, si une combinaison de membres d’une classe `Flag` n’active aucune option, l’évaluation booléenne de la comparaison est `False` :

    >>> from enum import Flag, auto
    >>> class Color(Flag):
    ...     RED = auto()
    ...     BLUE = auto()
    ...     GREEN = auto()
    ...
    >>> Color.RED & Color.GREEN
    <Color.0: 0>
    >>> bool(Color.RED & Color.GREEN)
    False

Les options de base doivent avoir des puissances de deux pour valeurs (1, 2, 4, 8, …) mais pas les combinaisons :

    >>> class Color(Flag):
    ...     RED = auto()
    ...     BLUE = auto()
    ...     GREEN = auto()
    ...     WHITE = RED | BLUE | GREEN
    ...
    >>> Color.WHITE
    <Color.WHITE: 7>

Donner un nom à la valeur « aucune option activée » ne change pas sa valeur booléenne :

    >>> class Color(Flag):
    ...     BLACK = 0
    ...     RED = auto()
    ...     BLUE = auto()
    ...     GREEN = auto()
    ...
    >>> Color.BLACK
    <Color.BLACK: 0>
    >>> bool(Color.BLACK)
    False

Note

Dans la plupart des cas, il est fortement recommandé d’utiliser `Enum` et `Flag` pour écrire du code nouveau, car `IntEnum` et `IntFlag` violent certains principes sémantiques d’une énumération (en pouvant être comparées à des entiers et donc, par transitivité, à d’autres énumérations). `IntEnum` et `IntFlag` ne doivent être utilisées que dans les cas où `Enum` et `Flag` ne suffisent pas ; par exemple quand des constantes entières sont remplacées par des énumérations, ou pour l’interopérabilité avec d’autres systèmes.

### 8.13.13.4. Autres

Bien que `IntEnum` fasse partie du module `enum`, elle serait très simple à implémenter hors de ce module :

    class IntEnum(int, Enum):
        pass

Ceci montre comment définir des énumérations dérivées similaires ; par exemple une classe `StrEnum` qui dériverait de `str` au lieu de `int`.

Quelques règles :

1. Pour hériter de `Enum`, les types de mélange doivent être placés avant la classe `Enum` elle-même dans la liste des classes de base, comme dans l’exemple de `IntEnum` ci-dessus.

2. Même si une classe `Enum` peut avoir des membres de n’importe quel type, dès lors qu’un type de mélange est ajouté, tous les membres doivent être de ce type, p. ex. `int` ci-dessus. Cette restriction ne s’applique pas aux types de mélange qui ne font qu’ajouter des méthodes et ne définissent pas de type de données, tels `int` ou `str`.

3. Quand un autre type de données est mélangé, l’attribut `value` n’est pas identique au membre de l’énumération lui-même, bien qu’ils soient équivalents et égaux en comparaison.

4. Formatage de style % : %s et %r appellent respectivement les méthodes `__str__()` et `__repr__()` de la classe `Enum` ; les autres codes, comme %i ou %h pour IntEnum, s’appliquent au membre comme si celui-ci était converti en son type de mélange.

5. Chaînes de caractères formatées littérales : `str.format()` et `format()` appellent la méthode `__format__()` du type de mélange. Pour appeler les fonctions `str()` ou `repr()` de la classe `Enum`, il faut utiliser les codes de formatage !s ou !r.

## 8.13.14. Exemples intéressants

Bien que `Enum`, `IntEnum`, `IntFlag` et `Flag` soient conçues pour répondre à la majorité des besoins, elles ne peuvent répondre à tous. Voici quelques recettes d’énumération qui peuvent être réutilisées telles quelles, ou peuvent servir d’exemple pour développer vos propres énumérations.

### 8.13.14.1. Omettre les valeurs

Dans de nombreux cas, la valeur réelle de l’énumération n’a pas d’importance. Il y a plusieurs façons de définir ce type d’énumération simple :

- affecter des instances de `auto` aux valeurs

- affecter des instances de `object` aux valeurs

- affecter des chaînes de caractères aux valeurs pour les décrire

- affecter un n-uplet aux valeurs et définir une méthode `__new__()` pour remplacer les n-uplets avec un `int`

Utiliser une de ces méthodes indique à l’utilisateur que les valeurs n’ont pas d’importance. Cela permet aussi d’ajouter, de supprimer ou de ré-ordonner les membres sans avoir à ré-énumérer les membres existants.

Quelle que soit la méthode choisie, il faut fournir une méthode `repr()` qui masque les valeurs (pas importantes de toute façon) :

    >>> class NoValue(Enum):
    ...     def __repr__(self):
    ...         return '<%s.%s>' % (self.__class__.__name__, self.name)
    ...

#### 8.13.14.1.1. Avec `auto`

On utilise `auto` de la manière suivante :

    >>> class Color(NoValue):
    ...     RED = auto()
    ...     BLUE = auto()
    ...     GREEN = auto()
    ...
    >>> Color.GREEN
    <Color.GREEN>

#### 8.13.14.1.2. Avec `object`

On utilise `object` de la manière suivante :

    >>> class Color(NoValue):
    ...     RED = object()
    ...     GREEN = object()
    ...     BLUE = object()
    ...
    >>> Color.GREEN
    <Color.GREEN>

#### 8.13.14.1.3. Avec une chaîne de caractères de description

On utilise une chaîne de caractères de la manière suivante :

    >>> class Color(NoValue):
    ...     RED = 'stop'
    ...     GREEN = 'go'
    ...     BLUE = 'too fast!'
    ...
    >>> Color.GREEN
    <Color.GREEN>
    >>> Color.GREEN.value
    'go'

#### 8.13.14.1.4. Avec une méthode ad-hoc `__new__()`

On utilise une méthode `__new__()` d’énumération de la manière suivante :

    >>> class AutoNumber(NoValue):
    ...     def __new__(cls):
    ...         value = len(cls.__members__) + 1
    ...         obj = object.__new__(cls)
    ...         obj._value_ = value
    ...         return obj
    ...
    >>> class Color(AutoNumber):
    ...     RED = ()
    ...     GREEN = ()
    ...     BLUE = ()
    ...
    >>> Color.GREEN
    <Color.GREEN>
    >>> Color.GREEN.value
    2

Note

La méthode `__new__()`, si définie, est appelée à la création des membres de l’énumération ; elle est ensuite remplacée par la méthode `__new__()` de Enum, qui est utilisée après la création de la classe pour la recherche des membres existants.

### 8.13.14.2. OrderedEnum

Une énumération ordonnée qui n’est pas basée sur `IntEnum` et qui, par conséquent, respecte les invariants classiques de `Enum` (comme par exemple l’impossibilité de pouvoir être comparée à d’autres énumérations) :

    >>> class OrderedEnum(Enum):
    ...     def __ge__(self, other):
    ...         if self.__class__ is other.__class__:
    ...             return self.value >= other.value
    ...         return NotImplemented
    ...     def __gt__(self, other):
    ...         if self.__class__ is other.__class__:
    ...             return self.value > other.value
    ...         return NotImplemented
    ...     def __le__(self, other):
    ...         if self.__class__ is other.__class__:
    ...             return self.value <= other.value
    ...         return NotImplemented
    ...     def __lt__(self, other):
    ...         if self.__class__ is other.__class__:
    ...             return self.value < other.value
    ...         return NotImplemented
    ...
    >>> class Grade(OrderedEnum):
    ...     A = 5
    ...     B = 4
    ...     C = 3
    ...     D = 2
    ...     F = 1
    ...
    >>> Grade.C < Grade.A
    True

### 8.13.14.3. DuplicateFreeEnum

Lève une erreur si un membre est dupliqué, plutôt que de créer un alias :

    >>> class DuplicateFreeEnum(Enum):
    ...     def __init__(self, *args):
    ...         cls = self.__class__
    ...         if any(self.value == e.value for e in cls):
    ...             a = self.name
    ...             e = cls(self.value).name
    ...             raise ValueError(
    ...                 "aliases not allowed in DuplicateFreeEnum:  %r --> %r"
    ...                 % (a, e))
    ...
    >>> class Color(DuplicateFreeEnum):
    ...     RED = 1
    ...     GREEN = 2
    ...     BLUE = 3
    ...     GRENE = 2
    ...
    Traceback (most recent call last):
    ...
    ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'

Note

Cet exemple d’héritage de Enum est intéressant pour ajouter ou modifier des comportements comme interdire les alias. Si vous ne souhaitez qu’interdire les alias, il suffit d’utiliser le décorateur `unique()`.

### 8.13.14.4. Planet

Si `__new__()` ou `__init__()` sont définies, la valeur du membre de l’énumération sera passée à ces méthodes :

    >>> class Planet(Enum):
    ...     MERCURY = (3.303e+23, 2.4397e6)
    ...     VENUS   = (4.869e+24, 6.0518e6)
    ...     EARTH   = (5.976e+24, 6.37814e6)
    ...     MARS    = (6.421e+23, 3.3972e6)
    ...     JUPITER = (1.9e+27,   7.1492e7)
    ...     SATURN  = (5.688e+26, 6.0268e7)
    ...     URANUS  = (8.686e+25, 2.5559e7)
    ...     NEPTUNE = (1.024e+26, 2.4746e7)
    ...     def __init__(self, mass, radius):
    ...         self.mass = mass       # in kilograms
    ...         self.radius = radius   # in meters
    ...     @property
    ...     def surface_gravity(self):
    ...         # universal gravitational constant  (m3 kg-1 s-2)
    ...         G = 6.67300E-11
    ...         return G * self.mass / (self.radius * self.radius)
    ...
    >>> Planet.EARTH.value
    (5.976e+24, 6378140.0)
    >>> Planet.EARTH.surface_gravity
    9.802652743337129

## 8.13.15. En quoi les Enums sont différentes ?

Les enums ont une métaclasse spéciale qui affecte de nombreux aspects des classes dérivées de Enum et de leur instances (membres).

### 8.13.15.1. Classes Enum

The `EnumMeta` metaclass is responsible for providing the `__contains__()`, `__dir__()`, `__iter__()` and other methods that allow one to do things with an `Enum` class that fail on a typical class, such as list(Color) or some_var in Color. `EnumMeta` is responsible for ensuring that various other methods on the final `Enum` class are correct (such as `__new__()`, `__getnewargs__()`, `__str__()` and `__repr__()`).

### 8.13.15.2. Membres d’Enum (c.-à-d. instances)

Il est intéressant de souligner que les membres d’une Enum sont des singletons. La classe `EnumMeta` les crée tous au moment de la création de la classe `Enum` elle-même et implémente une méthode `__new__()` spécifique. Cette méthode renvoie toujours les instances de membres déjà existantes pour être sûr de ne jamais en instancier de nouvelles.

### 8.13.15.3. Aspects approfondis

#### 8.13.15.3.1. Noms de la forme `__dunder__` disponibles

`__members__` est une `OrderedDict` de correspondances `nom_du_membre` / `membre`. Elle n’est disponible que depuis la classe.

La méthode `__new__()`, si elle est définie, doit créer et renvoyer les membres de l’énumération ; affecter correctement l’attribut `_value_` du membre est également conseillé. Une fois que tous les membres ont été créés, cette méthode n’est plus utilisée.

#### 8.13.15.3.2. Noms de la forme `_sunder_` disponibles

- `_name_` – nom du membre

- `_value_` – valeur du membre ; il est possible d’y accéder ou de la muer dans `__new__`

- `_missing_` – une fonction de recherche qui est appelée quand la valeur n’est pas trouvée ; elle peut être redéfinie

- `_order_` – utilisé en Python 2 ou 3 pour s’assurer que l’ordre des membres est cohérent (attribut de classe, supprimé durant la création de la classe)

- `_generate_next_value_` – utilisée par l” API par fonction et par `auto` pour obtenir une valeur appropriée à affecter à un membre de l’enum ; elle peut être redéfinie

Nouveau dans la version 3.6: `_missing_`, `_order_`, `_generate_next_value_`

Pour faciliter la transition de Python 2 en Python 3, l’attribut `_order_` peut être défini. Il sera comparé au véritable ordre de l’énumération et lève une erreur si les deux ne correspondent pas :

    >>> class Color(Enum):
    ...     _order_ = 'RED GREEN BLUE'
    ...     RED = 1
    ...     BLUE = 3
    ...     GREEN = 2
    ...
    Traceback (most recent call last):
    ...
    TypeError: member order does not match _order_

Note

En Python 2, l’attribut `_order_` est indispensable car l’ordre de la définition est perdu avant de pouvoir être enregistré.

#### 8.13.15.3.3. Type des membres de `Enum`

Les membres de `Enum` sont des instances de leur classe `Enum`. On y accède normalement par `ClasseEnum.membre`. Dans certains cas, on peut également y accéder par `ClasseEnum.membre.membre`, mais ceci est fortement déconseillé car cette indirection est susceptible d’échouer, ou pire, de ne pas renvoyer le membre de la classe `Enum` désiré (c’est une autre bonne raison pour définir tous les noms des membres en majuscules) :

    >>> class FieldTypes(Enum):
    ...     name = 0
    ...     value = 1
    ...     size = 2
    ...
    >>> FieldTypes.value.size
    <FieldTypes.size: 2>
    >>> FieldTypes.size.value
    2

Modifié dans la version 3.5.

#### 8.13.15.3.4. Valeur booléenne des classes `Enum` et de leurs membres

Les membres d’une classe `Enum` mélangée avec un type non dérivé de `Enum` (comme `int`, `str`, etc.) sont évalués selon les règles du type de mélange. Sinon, tous les membres valent `True`. Pour faire dépendre l’évaluation booléenne de votre propre Enum de la valeur du membre, il faut ajouter le code suivant à votre classe :

    def __bool__(self):
        return bool(self.value)

Les classes `Enum` valent toujours `True`.

#### 8.13.15.3.5. Classes `Enum` avec des méthodes

Si votre classe `Enum` contient des méthodes supplémentaires, comme la classe Planet ci-dessus, elles s’afficheront avec un appel à `dir()` sur le membre, mais pas avec un appel sur la classe :

    >>> dir(Planet)
    ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
    >>> dir(Planet.EARTH)
    ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']

#### 8.13.15.3.6. Combinaison de membres de `Flag`

Si une valeur issue de la combinaison de membres de Flag n’est pas associée explicitement à un membre, la fonction `repr()` inclut tous les membres et toutes les combinaisons de membres présents dans cette valeur :

    >>> class Color(Flag):
    ...     RED = auto()
    ...     GREEN = auto()
    ...     BLUE = auto()
    ...     MAGENTA = RED | BLUE
    ...     YELLOW = RED | GREEN
    ...     CYAN = GREEN | BLUE
    ...
    >>> Color(3)  # named combination
    <Color.YELLOW: 3>
    >>> Color(7)      # not named combination
    <Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>