当值在 Python 3.7 中可调用时,从枚举中获取所有值

Getting all values from an Enum, when values are callables in Python 3.7

我在 Python 3.7 中有一个 Enum class 定义如下:

# activation functions
def relu(x: float) -> float:
    return x * (x > 0)


def sigmoid(x: float) -> float:
    return 1 / (1 + math.exp(-x))


class Activation(Enum):
    SIGMOID = sigmoid
    RELU = relu

    def __call__(self, x):
        return self.value(x)

    @classmethod
    def values(cls):
        return [function.value for function in cls]

我在网上尝试了其他类似问题的一些方法,例如 list(Activation)Activation.values(),但总是得到一个空列表。有什么新想法吗?

中未提及的解决此问题的方法之一是不使用 functoolsstaticmethod 或包装函数。

使用 函数定义作为 Enum 成员的唯一值 的问题是 Enum 的 __init____new__ 永远不会被调用。有了额外的值,枚举会照常初始化。

证明如下:

import math
from enum import Enum


def relu(x: float) -> float:
    return x * (x > 0)


def sigmoid(x: float) -> float:
    return 1 / (1 + math.exp(-x))


class Activation(Enum):

    SIGMOID = 1, sigmoid
    RELU = 2, relu

    def __init__(self, *args):
        self.your_func = args[1]

    def __call__(self, x):
        return self.your_func(x)

    @classmethod
    def values(cls):
        return [member.your_func for member in cls]

print(Activation.SIGMOID(2))
print(Activation.RELU(2))
# 0.8807970779778823
# 2

for one_func in Activation.values():
    print(one_func(2))
# 0.8807970779778823
# 2

我不认为这是一个错误(正如评论中所建议的那样),因为:

However the issue with functions is that they are considered to be method definitions instead of attributes

几种解决方案的共同点是在 Enum(class 语法)值声明期间封装函数定义。

由于上述原因,使用没有 staticmethodfunctools 的 "wrapper function" 是行不通的,因为它永远不会被调用。尝试替换以下内容(__init____call__ 都不会被调用):

SIGMOID = sigmoid
RELU = relu

def __call__(self, *args, **kwargs):
    print("inside")
    return self.f(*args, **kwargs)

总而言之,也许是最 pythonic 的方法 是在声明时将函数定义包装在列表中,并在 __init__ 初始化时展开:

import math
from enum import Enum


def relu(x: float) -> float:
    return x * (x > 0)


def sigmoid(x: float) -> float:
    return 1 / (1 + math.exp(-x))


class Activation(Enum):

    SIGMOID = [sigmoid]
    RELU = [relu]

    def __init__(self, your_func):
        self.your_func = your_func[0]

    def __call__(self, x):
        return self.your_func(x)

    @classmethod
    def values(cls):
        return [member.your_func for member in cls]


print(Activation.SIGMOID(2))
print(Activation.RELU(2))
# 0.8807970779778823
# 2

for one_func in Activation.values():
    print(one_func(2))
# 0.8807970779778823
# 2

这节省了调用 staticmethod(有些人认为它在 class 定义之外使用非 pythonic)。并节省导入和调用 functools.partial - 它涉及每个成员访问的不必要的函数调用开销。

因此,上述方法可以说是最 pythonic 的解决方案。

如果您使用 aenum1 库,您可以使用其 member 描述符:

from aenum import Enum, member

class Activation(Enum):

    SIGMOID = member(sigmoid)
    RELU = member(relu)

    def __call__(self, x):
        return self.value(x)

    @classmethod
    def values(cls):
        return [function.value for function in cls]

也可以写成:

class Activation(Enum):

    @member
    def SIGMOID(x: float) -> float:
        return 1 / (1 + math.exp(-x))

    @member
    def RELU(x: float) -> float:
        return x * (x > 0)

    def __call__(self, x):
        return self.value(x)

    @classmethod
    def values(cls):
        return [function.value for function in cls]

1 披露:我是 Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) 库的作者。