[PYTHON] Ich habe ein Extenum-Paket erstellt, das die Enumeration erweitert

Hintergrund

Das Modul enum wurde aus Python 3.4 hinzugefügt. Ein Paket, das für Versionen unter 3.4 (einschließlich 2.x) zurückportiert wurde, ist bei PyPI als [enum34] registriert (https://pypi.python.org/pypi/enum34).

Wenn Sie das Enum-Modul noch nie verwendet haben, ist der folgende Artikel hilfreich.

Eine Notiz von Nick Coghlan, dem Hauptentwickler von CPython, bietet Hintergrundinformationen zur Standardbibliothek von enum und eine Zusammenfassung der damaligen Diskussionen.

Pythons Aufzählung kann mit * Good Enough * zufriedenstellend sein, da sie ursprünglich nicht Standard war.

Was fehlt im Gegenteil? Es gibt ein Buch mit dem Titel Effective Java, das wie eine Bibel in der Java-Welt aussieht und ein Kapitel über die Best Practices von enum abdeckt. Ich werde.

In der Java-Welt wird enum häufig verwendet, da es eine Vielzahl von Anwendungen bietet und eine praktische Funktion darstellt, z. B. die Gewährleistung der Typensicherheit beim Kompilieren und die Implementierung eines Singletons. Während ich mir Effective Java ansah, versuchte ich, die Funktionen zu erweitern, die Python enum nicht hat.

extenum

Ich habe ein [extenum] -Paket (https://pypi.python.org/pypi/extenum) erstellt, das das Standard-Enum-Modul erweitert.

Wie installiert man

$ pip install extenum

der Begriff

Gemäß offizielle Dokumententerminologie

>> from enum import Enum
>> class Color(Enum):

... red = 1 ... green = 2 ... blue = 3 ...

 > Kommentarbedingungen
 > * Die Klasse `Color` ist ein Aufzählungstyp (oder Enum).
 > * Die Attribute `Color.red`,` Color.green` und andere sind Mitglieder des Aufzählungstyps (oder Enum-Mitglieder).
 > * Mitglieder des Aufzählungstyps haben einen Namen und einen Wert (`Color.red` hat den Namen` red`,` Color.blue` hat den Wert 3 usw.).

 Die durch den Aufzählungstyp definierten Konstanten sind als *** Aufzählungstypelemente (oder Aufzählungselemente) *** definiert. In anderen Sprachen wird es auch als Enumerator oder Enumerationskonstante bezeichnet. Ich denke, dass Aufzählungskonstanten leichter zu verstehen sind, weil ich erwarte, dass sie eine Rolle als Konstanten spielen, aber in Übereinstimmung mit der offiziellen Dokumentation werde ich in diesem Dokument Aufzählungstypkonstanten als Aufzählungstypelemente bezeichnen.

### Ständige Implementierung fester Methoden
 Effektives Java wird als Konstante bezeichnet, aber diese * Konstante * bezieht sich auf ein Mitglied des Aufzählungstyps.

 Der Grund, warum ich mich für Extenum entschieden habe, war, dass dieser Mechanismus nicht verfügbar war. Als ich nach festen Konstantenmethoden suchte, fand ich [asym-enum](https://github.com/AsymmetricVentures/asym-enum#methods-and-functions) als so etwas.


#### **`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

Ich fand das etwas hässlich und beschloss, es selbst zu machen.

Mit extenum können Sie es folgendermaßen implementieren:

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

Ich konnte es ordentlich umsetzen.

Da Python keinen Überladungsmechanismus hat, implementiert * @ overload (CONSTANT) * eine feste Konstantenmethode mit einem Dekorator.

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

Feste konstante Methoden sind auch nützlich, um Absichten über den Namen der Methode zu kommunizieren. Schauen wir uns zum Beispiel einen Aufzählungstyp an, der Informationen zum Verarbeitungsstatus enthält.

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

Angenommen, jedes Aufzählungsmitglied zeigt auf einen Statuswert, aber Sie müssen feststellen, ob es sich um einen abbrechbaren Status handelt, bevor Sie diesen Vorgang abbrechen können.

Wenn es keine feste Konstantenmethode gibt, prüfen wir, ob das an eine Funktion übergebene Element des Aufzählungstyps entweder * WAITING * oder * RUNNING * ist.

3.4


def cancel(status):
    if status in [Status.WAITING, Status.RUNNING]:
        # do cancel

Dies erfordert, dass der Implementierer oder Leser der Abbruchfunktion die Details des Aufzählungstyps für Statusinformationen kennt und möglicherweise die Bedingungen in dieser Funktion ändern muss, wenn die Statusinformationen zunehmen.

Wenn Sie eine Methode mit fester Konstante haben, kann die Statusbeurteilung der Vorverarbeitung dieser Abbruchfunktion natürlicher und übersichtlicher geschrieben werden.

3.4


def cancel(status):
    if status.is_cancelable():
        # do cancel

Im Vergleich zum obigen Code ist es für Implementierer und Leser der Abbruchfunktion einfacher zu verstehen, und selbst wenn die Statusinformationen zunehmen, muss nur eine aufgezählte feste Konstantenmethode hinzugefügt werden.

Strategie Aufzählungsmuster

Dies ist ein Anwendungsbeispiel für die Implementierung einer konstanten festen Methode. Es ist für eine konservativere Implementierung vorgesehen, indem Aufzählungstypen von Tages- und Wochentags- / Feiertagsattributen verschachtelt werden.

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 ist als Übung nützlich, aber Python fühlt sich übertrieben (subjektiv). Ich habe versucht, es umzusetzen, aber ich denke, dass es ein subtiles Beispiel ist, von dem nicht gesagt werden kann, dass es gut oder schlecht ist.

Bitte sehen Sie, inwieweit Sie so etwas tun können.

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

Implizites Aufzählungsmitglied

Da die Mitglieder des Aufzählungstyps Klassenvariablen definieren, kann der folgende Aufzählungstyp nicht definiert werden.

3.4


class Color(Enum):
    red, green, blue

Wenn ich es ausführe, erhalte ich * NameError *.

3.4


NameError: name 'red' is not defined

Aufzählungsmodul hinzugefügt [PEP 435 - Keine Werte für Aufzählungen angeben müssen-](https://www.python.org/dev/peps/pep-0435/#not-having-to-specify-values-for- nach Aufzählungen)

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.

Gründe, warum es nicht möglich ist, einen impliziten Wert zu definieren, sind, dass die Implementierung magisch, auf den ersten Blick verwirrend und eher dem expliziten Zen als dem impliziten widerspricht.

Dies ist für die Python-Kultur korrekt, daher ist es in Ordnung, aber Nick Coghlans Hinweis Unterstützung für alternative Deklarationssyntaxen enum_creation.html (Unterstützung für alternative Deklarationssyntaxen) erwähnt, wie implizite Aufzählungsmitglieder implementiert werden.

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 ~~ Es ist eine interessante Methode für die Metaprogrammierung, also habe ich sie implementiert.

Die spezielle Methode __prepare__ wurde aus Python 3 hinzugefügt und die Metaklasse wird initialisiert. Hängt den Prozess der Rückgabe des Namespace dieser Klasse an, wenn __missing__ verknüpft die KeyError-Fehlerbehandlung für Wörterbuchunterklassen, wenn der Schlüssel nicht im Wörterbuch vorhanden ist Machen.

Sie können die Definition des Namespace einer Klasse verknüpfen, indem Sie ein Wörterbuch mit "missing" als Namespace-Objekt im vorherigen "prepare" zurückgeben.

Ich weiß nicht, wovon du sprichst, also lass es uns codieren.

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)

Verwenden Sie diese Metaklasse, um eine Klasse zu definieren.

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'

Beim Definieren einer Klasse wird die Methode newder Metaklasse * MyMetaclass * aufgerufen und * MyNameSpace * an den Klassennamensraum (* classdict *) übergeben. Der * MyNameSpace *missing` legt die Zeichenfolge * x * für Namen fest, die keinen Schlüssel haben, dh nicht im Klassennamensraum vorhanden sind.

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

Abgesehen davon wird der * @ overload * -Dekorator von * ConstantSpecificEnum * im vorherigen Abschnitt ebenfalls mit diesem Mechanismus implementiert. Ich denke, es gibt Vor- und Nachteile, diese Dekoratordefinition selbst zu verbergen, aber selbst wenn sie verborgen ist, fühlt sie sich von der Benutzerseite seltsam an, so dass * @ classmethod * und * @ staticmethod * global behandelt werden können. Ich fragte mich, ob es einen gab.

EnumSet EnumSet ist wie eine Utility-Klasse, die eine Menge mit Enum-Konstanten erstellt. Ich habe es implementiert, als ich mir Java's [EnumSet] angesehen habe (http://docs.oracle.com/javase/jp/8/docs/api/java/util/EnumSet.html).

Für leicht verständliche Zwecke schafft die Verwendung von EnumSet zur Implementierung von Verarbeitungen wie Flag-Operationen, die durch Bitarithmetik implementiert wurden, eine Atmosphäre, die für Menschen einfach ist.

3.4


>>> from enum import Enum
>>> class Mode(Enum):
...     READ = 4
...     WRITE = 2
...     EXECUTE = 1
... 

Versuchen wir, diesen Aufzählungstyp * Mode * zu verwenden.

3.4


>>> from extenum import EnumSet

>>> EnumSet.all_of(Mode)  #Generieren Sie ein EnumSet, das alle aufgezählten Mitglieder des Modus enthält
EnumSet({<Mode.READ: 4>, <Mode.WRITE: 2>, <Mode.EXECUTE: 1>})

>>> EnumSet.none_of(Mode)  #Generieren Sie ein leeres EnumSet, das ein aufgezähltes Mitglied von Mode akzeptiert
EnumSet()

>>> enumset = EnumSet.of(Mode.READ, Mode.EXECUTE)  #Generieren Sie ein EnumSet, das ein beliebiges Aufzählungsmitglied enthält
>>> 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

Das Zusammenstellen in einem EnumSet erleichtert die Handhabung von Modi und Optionen.

Zusammenfassung

Die folgenden Funktionen werden von extenum als Java-ähnliche Enum-Erweiterung bereitgestellt.

Es erbt und erweitert die Standards * Enum *, * EnumMeta * und * _EnumDict *, sodass es wahrscheinlich problemlos als regulärer Aufzählungstyp funktioniert.

Bitte lassen Sie uns wissen, ob es andere Aufzählungspraktiken gibt, die interessant sein könnten. Manchmal ist der Zweck, es zu machen, anstatt es zu benutzen.

Recommended Posts

Ich habe ein Extenum-Paket erstellt, das die Enumeration erweitert
Ich habe einen Ansible-Installer gemacht
Ich habe eine Android-App erstellt, die Google Map anzeigt
Ich habe einen Xubuntu-Server erstellt.
Ich habe ein Anomalieerkennungsmodell erstellt, das unter iOS funktioniert
Ich habe einen Anpanman-Maler diskriminiert
Ich habe ein Angular Starter Kit gemacht
Ich habe ein Paket erstellt, das morphologische Analysegeräte mit Python vergleichen kann
Ich habe eine KI erstellt, die ein Bild mit Saliency Map gut zuschneidet
Ich habe etwas gemacht, das sich bewegt (größere Reichweite)
Ich habe eine Online-Frequenzanalyse-App erstellt
Ich habe ein alternatives Modul für japandas.DataReader erstellt
Ich habe eine Chrome-Erweiterung erstellt, die ein Diagramm auf der Amedas-Seite anzeigt
Ich habe eine KI erstellt, die aus Trivia vorhersagt, und mich dazu gebracht, auf meine Trivia zu schließen. Hee-AI
Mit LINEBot habe ich eine Anwendung erstellt, die mich über die "Buszeit" informiert.
Ich habe ein Paket erstellt, um eine ausführbare Datei aus dem Hy-Quellcode zu erstellen
Hindernis (schwarz) Ich habe eine Raupe mit automatischer Vermeidung hergestellt.
Ich habe eine Amazon Web Service Dash-Schaltfläche erstellt
Ich habe eine Fehlermeldung erhalten, dass Python die Datei settings.ini nicht lesen konnte
Ich habe eine VM erstellt, auf der OpenCV für Python ausgeführt wird
Ich habe eine App erstellt, die mich warnt, wenn ich während des Studiums mit OpenCV mit meinem Smartphone herumspiele
Ich habe einen IFTTT-Knopf gemacht, der den Eingang 2-Sesam mit 1 Knopf (über AWS Lambda) entsperrt.
Ich habe mit Docker eine API erstellt, die den vorhergesagten Wert des maschinellen Lernmodells zurückgibt