Le module enum a été ajouté à partir de Python 3.4. Un package rétroporté pour les versions inférieures à 3.4 (y compris 2.x) est enregistré auprès de PyPI sous le nom enum34.
Si vous n'avez jamais utilisé le module enum, l'article suivant vous sera utile.
Une note de Nick Coghlan, le développeur principal de CPython, fournit un aperçu de la bibliothèque standard d'énum et un résumé des discussions à l'époque.
L'énumération de Python peut être satisfaisante avec * Good Enough * car elle n'était pas standard à l'origine.
Au contraire, que manque-t-il? Il existe un livre intitulé Effective Java qui ressemble à une bible dans le monde Java, et il couvre un chapitre sur les meilleures pratiques d'énum. Je vais.
Dans le monde Java, enum est souvent utilisé car il a une large gamme d'applications et est une fonction pratique, comme garantir la sécurité du type au moment de la compilation et implémenter un singleton. Donc, tout en regardant Effective Java, j'ai essayé d'étendre les fonctions que Python enum n'a pas.
extenum
J'ai créé un package extenum qui étend le module enum standard.
$ pip install extenum
Selon Terminologie du document officiel
>> from enum import Enum >> class Color(Enum):
... red = 1 ... green = 2 ... blue = 3 ...
> Termes de commentaire
> * La classe `Color` est un type d'énumération (ou Enum).
> * Les attributs `Color.red`,` Color.green` et autres sont membres du type énumération (ou membres Enum).
> * Les membres du type énumération ont un nom et une valeur (`Color.red` a un nom de` red`,` Color.blue` a une valeur de 3, etc.).
Les constantes définies par le type d'énumération sont définies comme *** membres de type d'énumération (ou membres d'énumération) ***. Dans d'autres langues, il est également appelé un énumérateur ou une constante d'énumération. Je pense que les constantes d'énumération sont plus faciles à comprendre car je m'attends à ce qu'elles jouent un rôle en tant que constantes, mais conformément à la documentation officielle, je ferai référence aux constantes de type énumération en tant que membres de type énumération dans cet article.
### Implémentation de méthode fixe constante
Le Java effectif est nommé comme une constante, mais cette * constante * fait référence à un membre du type énumération.
La raison pour laquelle j'ai décidé de faire de l'extenum était que ce mécanisme n'était pas disponible. Lorsque j'ai recherché des méthodes constantes fixes, j'ai trouvé [asym-enum](https://github.com/AsymmetricVentures/asym-enum#methods-and-functions) comme quelque chose comme ça.
#### **`3.4`**
```python
from asymm_enum.enum import Enum
class E(Enum):
A = 1
B = 2
def get_label(self):
''' instance methods are attached to individual members '''
return self.label
@classmethod
def get_b_label(cls):
''' class methods are attached to the enum '''
return cls.B.label
J'ai pensé que c'était un peu moche et j'ai décidé de le faire moi-même.
Avec extenum, vous pouvez l'implémenter comme ceci:
constant_specific_enum.py
from extenum import ConstantSpecificEnum
class Operation(ConstantSpecificEnum):
PLUS = '+'
MINUS = '-'
TIMES = '*'
DIVIDE = '/'
@overload(PLUS)
def apply(self, x, y):
return x + y
@overload(MINUS)
def apply(self, x, y):
return x - y
@overload(TIMES)
def apply(self, x, y):
return x * y
@overload(DIVIDE)
def apply(self, x, y):
return x / y
J'ai pu le mettre en œuvre proprement.
Puisque Python n'a pas de mécanisme de surcharge, * @ overload (CONSTANT)
* implémente une méthode de constante fixe avec un décorateur.
3.4
>>> from constant_specific_enum import Operation
>>> for name, const in Operation.__members__.items():
... print(name, ':', const.apply(2, 4))
PLUS : 6
MINUS : -2
TIMES : 8
DIVIDE : 0.5
Les méthodes à constante fixe sont également utiles pour communiquer l'intention par le nom de la méthode. Par exemple, examinons un type d'énumération qui contient des informations sur l'état du traitement.
3.4
from extenum import ConstantSpecificEnum
class Status(ConstantSpecificEnum):
PREPARING = 1
WAITING = 2
RUNNING = 3
@overload(PREPARING)
def is_cancelable(self):
return False
@overload(WAITING)
def is_cancelable(self):
return True
@overload(RUNNING)
def is_cancelable(self):
return True
Supposons que chaque membre d'énumération pointe vers une valeur de statut, mais vous devez déterminer s'il s'agit d'un statut annulable avant de pouvoir annuler ce processus.
S'il n'y a pas de méthode de constante fixe, nous vérifions si le membre du type d'énumération passé à une fonction est soit * WAITING * soit * RUNNING *.
3.4
def cancel(status):
if status in [Status.WAITING, Status.RUNNING]:
# do cancel
Cela nécessite que l'implémenteur ou le lecteur de la fonction d'annulation connaisse les détails du type d'énumération des informations d'état, et peut avoir besoin de modifier les conditions de cette fonction à mesure que les informations d'état augmentent.
Si vous avez une méthode de constante fixe, le jugement de statut du prétraitement de cette fonction d'annulation peut être écrit plus naturellement et proprement.
3.4
def cancel(status):
if status.is_cancelable():
# do cancel
Par rapport au code ci-dessus, il est plus facile pour les implémenteurs et les lecteurs de la fonction d'annulation de comprendre, et même si les informations d'état augmentent, il est seulement nécessaire d'ajouter une méthode de constante fixe énumérée.
Ceci est un exemple d'application d'implémentation de méthode fixe constante. Il est destiné à une implémentation plus conservatrice en imbriquant les types d'énumération des attributs jour et jour de la semaine / jour férié.
strategy_enum_pattern.py
from extenum import ConstantSpecificEnum
class PayrollDay(ConstantSpecificEnum):
class PayType(ConstantSpecificEnum):
WEEKDAY = 1
WEEKEND = 2
@overload(WEEKDAY)
def overtime_pay(self, hours, pay_rate):
return 0 if hours <= 8 else (hours - 8) * pay_rate / 2
@overload(WEEKEND)
def overtime_pay(self, hours, pay_rate):
return hours * pay_rate / 2
def pay(self, hours_worked, pay_rate):
base_pay = hours_worked * pay_rate
overtime_pay = self.overtime_pay(hours_worked, pay_rate)
return base_pay + overtime_pay
MONDAY = PayType.WEEKDAY
TUESDAY = PayType.WEEKDAY
WEDNESDAY = PayType.WEEKDAY
THURSDAY = PayType.WEEKDAY
FRIDAY = PayType.WEEKDAY
SATURDAY = PayType.WEEKEND
SUNDAY = PayType.WEEKEND
def pay(self, hours_worked, pay_rate):
return self.value.pay(hours_worked, pay_rate)
Java est utile comme pratique, mais Python se sent exagéré (subjectif). J'ai essayé de le mettre en œuvre, mais je pense que c'est un exemple subtil qui ne peut être qualifié de bon ou mauvais.
Veuillez voir dans la mesure où vous pouvez faire quelque chose comme ça.
3.4
>>> from strategy_enum_pattern import PayrollDay
>>> PayrollDay.MONDAY.pay(10, 1000.0)
11000.0
>>> PayrollDay.WEDNESDAY.pay(8, 1000.0)
8000.0
>>> PayrollDay.SATURDAY.pay(10, 1000.0)
15000.0
>>> PayrollDay.SUNDAY.pay(8, 1000.0)
12000.0
Étant donné que les membres du type d'énumération définissent des variables de classe, le type d'énumération suivant ne peut pas être défini.
3.4
class Color(Enum):
red, green, blue
Quand je l'exécute, j'obtiens * NameError *.
3.4
NameError: name 'red' is not defined
Ajout du module enum [PEP 435 -Ne pas avoir à spécifier de valeurs pour enums-](https://www.python.org/dev/peps/pep-0435/#not-having-to-specify-values-for- selon les énumérations)
Cons: involves much magic in the implementation, which makes even the definition of such enums baffling when first seen. Besides, explicit is better than implicit.
Les raisons pour lesquelles il n'est pas possible de définir une valeur implicite sont que l'implémentation est magique, déroutante à première vue, et plus contraire au Zen explicite qu'implicite.
C'est correct pour la culture Python, donc ça va, mais la note de Nick Coghlan Prise en charge des syntaxes de déclaration alternatives enum_creation.html # support-for-another-declaration-syntaxes) mentionne comment implémenter des membres d'énumération implicites.
Implicit enums that don’t really support normal code execution in the class body, and allow the above to be simplified further. It’s another variant of the autonumbered example in the test suite, but one that diverges substantially from normal Python semantics: merely mentioning a name will create a new reference to that name. While there are a number of ways to get into trouble when doing this, the basic concept would be to modify
__prepare__
on the metaclass to return a namespace that implements__missing__
as returning a custom sentinel value and overrides__getitem__
to treat repeating a name as an error:
Python ~~ Black Magic ~~ C'est une méthode intéressante pour la métaprogrammation, donc je l'ai implémentée.
La méthode spéciale __prepare__
a été ajoutée à partir de Python 3 et la métaclasse est initialisée. Accroche le processus de retour de l'espace de noms de cette classe lorsque __missing__
accroche la gestion des erreurs KeyError pour les sous-classes du dictionnaire lorsque la clé n'existe pas dans le dictionnaire Faire.
Vous pouvez accrocher la définition de l'espace de noms d'une classe en renvoyant un dictionnaire avec «missing» comme objet d'espace de noms dans le précédent «prepare».
Je ne sais pas de quoi vous parlez, alors codons-le.
prepare_missing.py
class MyNameSpace(dict):
def __missing__(self, key):
self[key] = value = 'x'
return value
class MyMetaclass(type):
@classmethod
def __prepare__(metacls, cls, bases):
return MyNameSpace()
def __new__(metacls, cls, bases, classdict):
print('classdict is', classdict.__class__)
return super().__new__(metacls, cls, bases, classdict)
Utilisez cette métaclasse pour définir une classe.
3.4
>>> from prepare_missing import MyMetaclass
>>> class Color(metaclass=MyMetaclass):
... red, green, blue
...
classdict is <class 'status.MyNameSpace'>
>>> Color.red
'x'
>>> Color.blue
'x'
Lors de la définition d'une classe, la méthode __new__
de la métaclasse * MyMetaclass * est appelée et * MyNameSpace * est passé à l'espace de noms de classe (* classdict *). Le * MyNameSpace * __missing__
définit la chaîne * x * pour les noms qui n'ont pas de clé, c'est-à-dire qui n'existent pas dans l'espace de noms de classe.
3.4
>>> from extenum import ImplicitEnum
>>> class Color(ImplicitEnum):
... RED
... GREEN
... BLUE
...
>>> for name, const in Color.__members__.items():
... print(name, ':', const.value)
...
RED : 1
GREEN : 2
BLUE : 3
En passant, le décorateur * @ overload
* de * ConstantSpecificEnum * dans la section précédente est également implémenté en utilisant ce mécanisme. Je pense qu'il y a des avantages et des inconvénients à masquer cette définition de décorateur elle-même, mais même si elle est cachée, cela semble étrange du côté de l'utilisateur afin que * @ classmethod
* et * @ staticmethod
* puissent être gérés globalement. Je me suis demandé s'il y en avait un.
EnumSet EnumSet est comme une classe utilitaire qui produit un ensemble contenant des constantes Enum. Je l'ai implémenté en regardant [EnumSet] de Java (http://docs.oracle.com/javase/jp/8/docs/api/java/util/EnumSet.html).
À des fins faciles à comprendre, l'utilisation d'EnumSet pour implémenter des traitements tels que des opérations d'indicateur, qui a été implémentée par arithmétique binaire, crée une atmosphère facile pour les humains.
3.4
>>> from enum import Enum
>>> class Mode(Enum):
... READ = 4
... WRITE = 2
... EXECUTE = 1
...
Essayons d'utiliser ce type d'énumération * Mode *.
3.4
>>> from extenum import EnumSet
>>> EnumSet.all_of(Mode) #Générer un EnumSet contenant tous les membres énumérés en mode
EnumSet({<Mode.READ: 4>, <Mode.WRITE: 2>, <Mode.EXECUTE: 1>})
>>> EnumSet.none_of(Mode) #Générer un EnumSet vide qui accepte un membre énuméré de Mode
EnumSet()
>>> enumset = EnumSet.of(Mode.READ, Mode.EXECUTE) #Générer un EnumSet contenant n'importe quel membre d'énumération
>>> enumset
EnumSet({<Mode.READ: 4>, <Mode.EXECUTE: 1>})
>>> enumset.update(EnumSet.of(Mode.READ, Mode.WRITE))
>>> enumset
EnumSet({<Mode.READ: 4>, <Mode.WRITE: 2>, <Mode.EXECUTE: 1>})
>>> Mode.WRITE in enumset
True
>>> 2 in enumset
False
Les rassembler dans un EnumSet facilite la gestion des modes et des options.
Les fonctionnalités suivantes sont fournies par extenum en tant qu'extension enum de type Java.
Il hérite et étend les standards * Enum *, * EnumMeta * et * _EnumDict *, donc il fonctionne probablement comme un type d'énumération standard.
Veuillez nous faire savoir s'il existe d'autres pratiques d'énumération qui pourraient être intéressantes. Parfois, le but est de le fabriquer plutôt que de l'utiliser.
Recommended Posts