Je connais les classes de base Enum
et IntEnum
. Les deux sont très utiles mais je manque des fonctionnalités pour les opérations de drapeau. Je ne m'attends pas à ce que ces deux classes implémentent ma fonctionnalité souhaitée.
Construisons un exemple:
class NetlistKind(IntEnum):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15
Comme vous pouvez le voir, j'utilise déjà IntEnum
pour obtenir des fonctionnalités arithmétiques pour cette énumération. Ce serait bien d'avoir quelque chose comme @unique
pour garantir que toutes les valeurs sont une puissance de deux. Je peux le faire en forçant enum.unique à mes besoins. (Je sais que All
est une exception à cette règle.)
Comment une telle énumération est-elle utilisée?
filter = NetlistKind.LatticeNetlist | NetlistKind.QuartusNetlist
Grâce au sous-jacent, les opérations int bit sont possibles et le filtre a une valeur interne de 3.
Si ce serait bien d'avoir une fonction "is flag X set in filter Y" ou encore mieux un opérateur. J'ajoute une fonction magique pour x in y
:
@unique
class NetlistKind(IntEnum):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15
def __contains__(self, item):
return (self.value & item.value) == item.value
Exemple d'utilisation:
....
def GetNetlists(self, filter=NetlistKind.All):
for entity in self._entities:
for nl in entity.GetNetlists():
if (nl.kind in filter):
yield nl
def GetXilinxNetlists(self):
return self.GetNetlists(NetlistKind.XSTNetlist | NetlistKind.CoreGenNetlist)
Les questions sont donc:
Fonctionnalités ouvertes:
__str__
J'ai récemment publié un paquet open source py-flags qui vise ce problème. Cette bibliothèque possède exactement cette fonctionnalité et sa conception est fortement influencée par le module d'énumération python3.
Il y a des débats pour savoir s'il est suffisamment pythonique pour implémenter une telle classe de drapeaux parce que sa fonctionnalité a d'énormes chevauchements avec d'autres méthodes fournies par le langage (collection de variables booléennes, ensembles, objets avec attributs booléens ou dict avec éléments booléens, ...) . Pour cette raison, je pense qu'une classe de drapeaux est trop étroite et/ou redondante pour se rendre à la bibliothèque standard, mais dans certains cas, elle est bien meilleure que les solutions répertoriées précédemment, donc avoir une bibliothèque compatible "pip install" peut venir en pratique.
Votre exemple ressemblerait à ceci en utilisant le module py-flags:
from flags import Flags
class NetlistKind(Flags):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15
Les choses ci-dessus pourraient être modifiées un peu plus car une classe de drapeaux déclarée avec la bibliothèque fournit automatiquement deux drapeaux "virtuels": NetlistKind.no_flags
Et NetlistKind.all_flags
. Ceux-ci rendent les NetlistKind.Unknown
Et NetlistKind.All
Déjà déclarés redondants afin que nous puissions les exclure de la déclaration, mais le problème est que no_flags
Et all_flags
Ne correspondent pas votre convention de dénomination. Pour faciliter cela, nous déclarons une classe de base de drapeaux dans votre projet au lieu de flags.Flags
Et vous devrez l'utiliser dans le reste de votre projet:
from flags import Flags
class BaseFlags(Flags):
__no_flags_name__ = 'Unknown'
__all_flags_name__ = 'All'
Sur la base de la classe de base précédemment déclarée qui peut être sous-classée par l'un de vos indicateurs dans votre projet, nous pourrions changer votre déclaration d'indicateur en:
class NetlistKind(BaseFlags):
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
De cette façon, NetlistKind.Unknown
Est automatiquement déclaré avec une valeur de zéro. NetlistKind.All
Est également là et c'est automatiquement la combinaison de tous vos drapeaux déclarés. Il est possible d'itérer les membres enum avec/sans ces indicateurs virtuels. Vous pouvez également déclarer des alias (indicateurs qui ont la même valeur qu'un autre indicateur précédemment déclaré).
Comme déclaration alternative en utilisant le "style d'appel de fonction" (également fourni par le module d'énumération standard):
NetlistKind = BaseFlags('NetlistKind', ['LatticeNetlist', 'QuartusNetlist',
'XSTNetlist', 'CoreGenNetlist'])
Si une classe flags déclare certains membres, elle est considérée comme finale. Toute tentative de sous-classe entraînera une erreur. Il n'est pas sémantiquement souhaitable d'autoriser le sous-classement d'une classe d'indicateur dans le but d'ajouter de nouveaux membres ou de modifier la fonctionnalité.
En plus de cela, la classe flags fournit les opérateurs listés (opérateurs booléens, dans, itération, etc ...) de manière sécurisée. Je vais terminer le fichier README.rst avec un peu de plomberie sur l'interface du package dans les prochains jours, mais la fonctionnalité de base est déjà là et testée avec une assez bonne couverture.
Python 3.6 a ajouté Flag
et IntFlag
qui prennent en charge les opérations bit à bit habituelles. En prime, les valeurs résultantes des opérations bit par bit sont toujours membres de la classe de drapeau d'origine et sont des singletons [1].
La bibliothèque aenum
a également cet ajout et est réutilisable à Python 2.7.
[1] Un bogue existe dans la version 3.6.0: si les membres du pseudo-drapeau sont créés dans des threads, il peut y avoir des doublons; cela est corrigé dans 3.6.1 (et n'a jamais existé dans aenum
).