Ich habe eine Kuriosität in den Python 3-Enums festgestellt (Link).
Wenn Sie den Wert einer Aufzählung auf eine Funktion festlegen, wird verhindert, dass das Attribut als Aufzählungsobjekt umbrochen wird, sodass Sie die coolen Funktionen wie EnumCls['AttrName'] nicht verwenden können, um das Attribut dynamisch zu laden.

Ist das ein Fehler? Absichtlich gemacht?
Ich habe eine Weile gesucht, aber keine Erwähnung von eingeschränkten Werten gefunden, die Sie in einer Aufzählung verwenden können.

Hier ist ein Beispielcode, der das Problem anzeigt:

class Color(Enum):
    Red = lambda: print('In Red')
    Blue = lambda: print('In Blue')

print(Color.Red)    # <function> - should be Color.Red via Docs
print(Color.Blue)   # <function> - should be Color.Bluevia Docs
print(Color['Red']) # throws KeyError - should be Color.Red via Docs

Außerdem frage ich zum ersten Mal, also lass es mich wissen, wenn ich etwas anders machen sollte! Und danke für die Hilfe!

18
C. Loew 9 Aug. 2015 im 20:41

3 Antworten

Beste Antwort

In der Dokumentation heißt es:

Die Regeln für das, was erlaubt ist, lauten wie folgt: _sunder_ Namen (beginnend und endend mit einem einzelnen Unterstrich) werden von enum reserviert und können nicht verwendet werden; Alle anderen in einer Aufzählung definierten Attribute werden Mitglieder dieser Aufzählung, mit Ausnahme von __dunder__ Namen und Deskriptoren (Methoden sind auch Deskriptoren).

Eine "Methode" ist nur eine Funktion, die in einem Klassenkörper definiert ist. Es spielt keine Rolle, ob Sie es mit lambda oder def definieren. Ihr Beispiel ist also dasselbe wie:

class Color(Enum):
    def Red():
        print('In Red')
    def Blue():
        print('In Blue')

Mit anderen Worten, Ihre angeblichen Aufzählungswerte sind tatsächlich Methoden und werden daher nicht Mitglieder der Aufzählung.

10
BrenBarn 9 Aug. 2015 im 17:50

Wenn jemand Enum mit Funktionen als Werte verwenden muss / möchte, ist dies möglich, indem ein aufrufbares Objekt als Proxy verwendet wird.

class FunctionProxy:
    """Allow to mask a function as an Object."""
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        return self.function(*args, **kwargs)

Ein einfacher Test:

from enum import Enum
class Functions(Enum):
    Print_Function = FunctionProxy(lambda *a: print(*a))
    Split_Function = FunctionProxy(lambda s, d='.': s.split(d))

Functions.Print_Function.value('Hello World!')
# Hello World!
Functions.Split_Function.value('Hello.World.!')
# ['Hello', 'World', '!']
2
Ceppo93 8 Nov. 2016 im 12:28

Sie können diese Lambdas in Tupel einwickeln und die __call__ -Methode überschreiben:

class Color(Enum):
    red = (lambda text: '<font color=red>%s</font>' % text,)
    blue = (lambda text: '<font color=blue>%s</font>' % text,)

    def __call__(self, *args, **kwargs):
        return self.value[0](*args, **kwargs)

Kann dann verwendet werden:

>>> Color.red('flowers')
<font color=red>flowers</font>
6
K3---rnc 23 Nov. 2016 im 21:30