web-dev-qa-db-fra.com

Quelle est la différence entre enum et namedtuple?

Je voudrais savoir quelles sont les différences entre enum et namedtuple et quand on devrait utiliser l'un sur l'autre.

26
Carlos Afonso

Comme analogie (quoique imparfaite), vous pouvez penser à enum.Enum et namedtuple in python as enum and struct in C. En d'autres termes, enums est un moyen des valeurs d'alias, tandis que namedtuple est un moyen d'encapsuler les données par leur nom. Les deux ne sont pas vraiment interchangeables, et vous pouvez utiliser enums comme valeurs nommées dans un namedtuple.

Je pense que cet exemple illustre la différence.

from collections import namedtuple
from enum import Enum

class HairColor(Enum):
    blonde = 1
    brown = 2
    black = 3
    red = 4

Person = namedtuple('Person', ['name','age','hair_color'])
bert = Person('Bert', 5, HairColor.black)

Vous pouvez accéder aux "attributs" nommés de la personne de la même manière qu'un objet normal.

>>> print(bert.name)
Bert
>>> print(bert.age)
5
>>> print(bert.hair_color)
HairColor.black
>>> print(bert.hair_color.value)
3

Souvent, vous ne voyez pas les namedtuple comme ceci car le même concept essentiel peut être accompli en utilisant la déclaration class plus largement connue. La définition class ci-dessous se comporte presque de manière identique à la définition namedtuple ci-dessus.

class Person:
    def __init__(self, name, age, hair_color):
        self.name = name
        self.age = age
        self.hair_color = hair_color

Cependant, une différence majeure entre un objet namedtuple et un objet class est que les attributs d'un namedtuple ne peuvent pas être modifiés après sa création.

27
Billy

namedtuple est une structure rapide qui, en utilisant __ slots __ au lieu de __ dict __ , finalise le contenu que vous fournissez à l'initialisation (qui devient pratiquement en lecture seule, bien qu'il existe une méthode _replace ()).

Un tuple nommé est généralement utilisé lorsque vous avez besoin de nombreux (tels que des centaines, des milliers et même des millions) d'objets du même type ou lorsque vous lisez et/ou écrivez un enregistrement.
Par exemple, un exemple souvent cité est un point nommétuple qui pourrait être utilisé pour travailler avec un sommet de polygone avec son x, y, z Composants.
La surcharge introduite par un tuple nommé sur un tuple standard est minime si on la compare à l'avantage de toujours pointer le bon composant par son nom (.x, .y, .z, .. .) au lieu de par index (0, 1, 2, ...).
La lecture de code tel que A.x est plus facile que A [0]: la signification est évidente, même des mois après avoir écrit le code et, mieux encore, pour d'autres programmeurs.

Ainsi, un tuple nommé est rapide, peut être utilisé pour identifier de manière significative le contenu du tuple et, enfin et surtout, peut coexister avec un code plus ancien accédant à un contenu de tuple par index.

from collections import namedtuple

Point = namedtuple('Point', 'x y z')  # note the x, y, z fields

Origin = Point(0, 0, 0)

A = Point(1, 1, 1)
B = Point(1, 1, 0)
C = Point(1, 0, 0)
D = Point(1, 2, 3)

for p in (Origin, A, B, C, D):
    print(p)
    print('x:', p.x, '  y:', p.y, '  z:', p.z)
    print('x:', p[0], '  y:', p[1], '  z:', p[2])
    print()

En partant de l'exemple ci-dessus, dès que tout accède aux composants de points par nom plutôt que par index, d'autres modifications pourraient être introduites plus facilement, en n'entrant dans la modification d'aucun numéro d'index:

from collections import namedtuple


Point = namedtuple('Point', 'name x y z')  # addition of the field 'name'

Origin = Point('O', 0, 0, 0)

A = Point('A', 1, 1, 1)
B = Point('B', 1, 1, 0)
C = Point('C', 1, 0, 0)
D = Point('D', 1, 0, 1)

for p in (Origin, A, B, C, D):
    print(p)
    print(p.name)  # more readable than p[0] that is no more the x coordinate
    print('x:', p.x,  '  y:', p.y,  '  z:', p.z)  # unchanged
    print('x:', p[1], '  y:', p[2], '  z:', p[3])  # changed
    print()

Une énumération est un moyen de coupler des noms symboliques à des valeurs constantes et de les classer comme un ensemble spécifique. Nous définissons une énumération en créant une classe dérivée de Enum ou IntEnum , en fonction des valeurs que nous voulons que nos constantes aient: Enum est la version générique, IntEnum impose le fait que chaque valeur constante sera de type int.

Par exemple, les énumérations sont bonnes pour définir les couleurs par nom, types d'entiers spécifiques, sexe ou, encore une fois - plus généralement - éléments appartenant à un ensemble spécifique.

from enum import Enum, IntEnum, unique

class Color_1(Enum):
    red = 'red'
    green = 'green'
    blue = 'blue'

class Color_2(Enum):
    red = (255, 0, 0)
    green = (0, 255, 0)
    blue = (0, 0, 255)

class Color_3(IntEnum):
    red = 0xFF0000
    green = 0xFF00
    blue = 0xFF

class Gender_1(Enum):
    unknown = 'U'
    male = 'M'
    female = 'F'

class Gender_2(Enum):
    unknown = 0.3
    male = 0.5
    female = 0.7

class Shape(Enum):  # Note the different constants types, perfectly legal
    TRIANGLE = 't'
    RECTANGLE = 5
    SQUARE = Tuple('square')

class DataType(IntEnum):
    int8 = -8
    int16 = -16
    int32 = -32
    int64 = -64
    int = -2
    negative = -1
    positive = 1
    uint = 2
    uint8 = 8
    uint16 = 16
    uint32 = 32
    uint64 = 64

Dans le développement Pythonic - les éléments d'énumération peuvent avoir une valeur spécifique assignée - qui peut être unique ou non, selon vos préférences et vos spécifications. Le décorateur nique est utilisé pour appliquer l'unicité des valeurs. Par défaut, il est possible d'attribuer la même valeur constante à deux ou plusieurs noms symboliques différents.

class Color_4(IntEnum):
    red = 1
    green = 2
    blue = 3
    RED = 1
    GREEN = 2
    BLUE = 3

Les éléments d'énumération peuvent être comparés entre eux, mais pour qu'ils réussissent, non seulement la valeur doit correspondre, même leur type doit être le même.

Par exemple:

Color_4.red == Color_4.RED

renverra True (même classe, même valeur), mais ce qui suit:

Shape.SQUARE == Tuple('square')

sera False - car l'élément droit de la comparaison - Tuple ('square') - n'est pas de type Shape, bien que les deux aient la même valeur.

Pour conclure, les énumérations et les nommés sont des instruments différents.

Des énumérations ont été ajoutées récemment à Python (recherche PEP435). Si la mémoire me convient, les nommés sont disponibles depuis assez longtemps, mais je suis toujours un débutant dans la communauté, donc je peux me tromper HTH

16
Alberto Vassena