2016-04-24 20 views
7

Conosco le classi base Enum e IntEnum. Entrambi sono molto utili ma mi mancano le funzionalità per le operazioni di bandiera. Non mi aspetto che queste due classi implementino la mia funzionalità desiderata.Esiste una classe Python/enum per operazioni flag/bit mask?

Costruiamo un esempio:

class NetlistKind(IntEnum): 
    Unknown = 0 
    LatticeNetlist = 1 
    QuartusNetlist = 2 
    XSTNetlist = 4 
    CoreGenNetlist = 8 
    All = 15 

Come potete vedere, sto già utilizzando IntEnum per ottenere funzioni aritmetiche per questo enum. Sarebbe bello avere qualcosa come @unique per garantire che tutti i valori siano una potenza di due. Posso farlo forking enum.unique per i miei bisogni. (Sono consapevole del fatto che All è un'eccezione da quella regola)

Come viene utilizzata questa enumerazione?

filter = NetlistKind.LatticeNetlist | NetlistKind.QuartusNetlist 

Grazie alle operazioni underlaying int bit sono possibili e filtro ha un valore interno 3.

Se sarebbe bello avere un "è bandiera X insieme filtro Y" funzione o meglio ancora un operatore. Aggiungo una funzione magico per x in y: ad esempio

@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 

Usage:

.... 
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) 

Quindi le domande sono:

  • ci sono modi migliori per implementare campi di bit?
  • Sono i modi migliori per implementare un filtro 1-D simile? Non voglio usare lamdas per una condizione di filtro così semplice?
  • Questa soluzione è già inclusa nella libreria standard Python?
  • Come aggiungere questa estensione enum alla prossima versione di Python? :)

caratteristiche aperte:

  • restituirà un elenco di tutte le bandiere attive nel __str__
  • ...?
+1

Ho recentemente attrezzato una mia biblioteca di bandiere con i test unitari e l'ho pubblicata su pypi. Sto per finire è README.rst e aggiungere alcune funzionalità extra nei prossimi giorni. La sua interfaccia è fortemente influenzata dal modulo enum standard di python3. Dai un'occhiata se sei interessato: https://pypi.python.org/pypi/py-flags Ho visto discussioni sul fatto che le bandiere siano o meno un approccio pitonico. I miei futuri aggiornamenti al README.rst avranno una sezione che discuterà i pro ei contro dell'utilizzo di diversi bool come funzioni args o memorizzazione di bool in un oggetto o dict VS usando set VS usando flags. – pasztorpisti

+1

Si prega di inviare il tuo commento come risposta, quindi posso inversione di tendenza! Sembra molto buono e maturo. Solo una domanda: perché devo dare un FQN all'enum? Esempio: 'TextStyle ('TextStyle.bold')'.Penso che 'bold' sia sufficiente, perché lo spazio dei nomi è già limitato a' TextStyle', perché lo passi nel suo costruttore. – Paebbels

+1

Link solo le risposte non sono benvenute su SO Mi spiace ... Il 'str()' dell'enumerazione può essere usato in altri contesti non solo in caso di serializzazione, questo è il motivo per cui '__str__' restituisce fqdn. Penso che 'str()' dovrebbe essere interpretabile anche senza la classe flags nel contesto. In realtà ai fini della serializzazione personalizzata ho fornito un 'to_simple_str()' oltre allo standard '__str__'. In questo caso 'to_simple_str()' emetterebbe semplicemente ''bold'' e' TextStyle (' bold ') 'funzionerebbe anche. In realtà il supporto serializzatore pickle di flag salva solo il nome della classe flags e l'output di 'to_simple_str()'. – pasztorpisti

risposta

5

Ho recentemente pubblicato un pacchetto opensource py-flags che mira a questo problema. Quella libreria ha esattamente questa funzionalità e il suo design è fortemente influenzato dal modulo enum python3.

Ci sono discussioni sul fatto che sia abbastanza pitone da implementare una classe di questo tipo perché la sua funzionalità ha enormi sovrapposizioni con altri metodi forniti dal linguaggio (raccolta di variabili bool, set, oggetti con attributi bool o dit con elementi bool, ...). Per questo motivo ritengo che una classe flags sia troppo ristretta e/o ridondante per raggiungere la libreria standard, ma in alcuni casi è molto meglio delle soluzioni elencate in precedenza, quindi è possibile disporre di una libreria "pip install". utile.

Il vostro esempio sarà simile alla seguente utilizzando il modulo py-flags:

from flags import Flags 

class NetlistKind(Flags): 
    Unknown = 0 
    LatticeNetlist = 1 
    QuartusNetlist = 2 
    XSTNetlist = 4 
    CoreGenNetlist = 8 
    All = 15 

Le cose di cui sopra potrebbero essere ottimizzato un po 'oltre, perché una classe bandiere dichiarato con la libreria fornisce automaticamente due bandiere "virtuali": NetlistKind.no_flags e NetlistKind.all_flags. Questi rendono il già dichiarato NetlistKind.Unknown e NetlistKind.All ridondante in modo che potremmo lasciarli fuori dalla dichiarazione, ma il problema è che no_flags e all_flags non corrispondono alla convenzione di denominazione. Per aiutare questo abbiamo dichiarare una classe base bandiere nel progetto, invece di flags.Flags ed avrete da usare che in tutto il resto del progetto:

from flags import Flags 

class BaseFlags(Flags): 
    __no_flags_name__ = 'Unknown' 
    __all_flags_name__ = 'All' 

In base alla classe di base precedentemente dichiarato che possono essere sottoclassi da una delle le vostre bandiere nel progetto che potrebbe cambiare la vostra dichiarazione di bandiera:

class NetlistKind(BaseFlags): 
    LatticeNetlist = 1 
    QuartusNetlist = 2 
    XSTNetlist = 4 
    CoreGenNetlist = 8 

in questo modo NetlistKind.Unknown è dichiarata automaticamente con un valore pari a zero. NetlistKind.All è anche lì ed è automaticamente la combinazione di tutti i tuoi flag dichiarati. È possibile iterare membri di enum con/senza questi flag virtuali. Puoi anche dichiarare alias (flag che hanno lo stesso valore di un altro flag precedentemente dichiarato).

come dichiarazione alternativa utilizzando il "stile funzione di chiamata" (fornito anche dal modulo enum standard):

NetlistKind = BaseFlags('NetlistKind', ['LatticeNetlist', 'QuartusNetlist', 
             'XSTNetlist', 'CoreGenNetlist']) 

Se una classe bandiere dichiara alcuni membri, allora è considerata definitiva. Provare a creare una sottoclasse provocherà un errore. È semanticamente indesiderato per consentire la creazione di sottoclassi di una classe flag allo scopo di aggiungere nuovi membri o cambiare funzionalità.

Oltre a ciò, la classe flags fornisce agli operatori il vostro elenco (operatori bool in, iterazione, ecc.) In un modo sicuro per i tipi. Finirò il README.rst insieme a un piccolo plumbing sull'interfaccia del pacchetto nei prossimi giorni, ma la funzionalità di base è già presente e testata con una copertura abbastanza buona.

+0

Argh, avrei dovuto iniziare la taglia prima che accettassi la tua risposta. Ora il collegamento è andato :(. Quindi praticamente +100 per il lavoro complessivo e il suggerimento su una classe base definita dall'utente per sovrascrivere i nomi '* _flags'. Funziona come un fascino. – Paebbels

+0

@Paebbels Grazie per essere generosi ma io ' di solito non sono qui per i punti.Vedo lo stackoverflow quando sono troppo stanco per fare altre cose .-D Sto lavorando alla lib e si tratta del file README.rst. Un documento più dettagliato con una migliore descrizione dell'interfaccia e un po ' La filosofia di progettazione sarà presto disponibile nei prossimi giorni.La documentazione attuale è super base come avete visto, ma anche in caso di documenti più grandi, di solito inizio con una sezione TL, DR simile ... Sondare la pagina pypi o github del lib se sei interessato alla versione più lunga imminente – pasztorpisti

+0

A volte aggiungo un premio per onorare il buon lavoro, specialmente quando mi risparmia un po 'di tempo per scriverlo da solo :). Quindi la tua lib ora è usata in "The PoC-Library". Penso che dovrei trovare un buon modo per descrivere le dipendenze di PoC. Non abbiamo setup.py perché PoC non è installato. Gli script Python sono solo un'infrastruttura di sottofondo ... Dovrei scrivere una nuova domanda per questo ... – Paebbels

12

Python 3.6 ha aggiunto Flag e IntFlag che supportano le consuete operazioni di bit-saggio. Come bonus, i valori risultanti dalle operazioni bit-saggio sono ancora membri della classe flag originale e sono singleton [1].

La libreria aenum ha anche questa aggiunta ed è utilizzabile su Python 2.7.

[1] Un bug esiste in 3.6.0: se i membri di psuedo-flag sono stati creati in thread, potrebbero esserci dei duplicati; questo è corretto in 3.6.1 (e non è mai esistito in aenum).

+0

Grazie a Ethan. Il modulo py-flags fornito da @pasztorpisti è molto potente. Forse Python 3.6 dovrebbe esaminare il suo modulo (basato su metaclassi) e incorporare alcune funzionalità. => https://github.com/pasztorpisti/py-flags – Paebbels

+1

@Paebbels: Sembra che sia abbastanza simile alla versione stdlib. –

+2

@Paebbels Quando si tratta delle funzionalità di base sono quasi equivalenti, quindi gli utenti di python3.6 + dovrebbero prendere in considerazione la versione di std lib. 'py-flags' contiene alcune caratteristiche extra che possono essere considerate buone o cattive e gli sviluppatori possono aggiungere queste funzionalità alla versione di std lib se necessario utilizzando l'ereditarietà. 'Py-flags' evita deliberatamente di derivare il' Flags' da 'Enum' come una decisione progettuale. Penso che la relazione corretta tra enumerazione e bandiere (se necessario) dovrebbe assomigliare alla relazione tra enum e tipi di enum di pascal: http://wiki.freepascal.org/Set – pasztorpisti