当值在 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()
,但总是得到一个空列表。有什么新想法吗?
中未提及的解决此问题的方法之一是不使用 functools
、staticmethod
或包装函数。
使用 函数定义作为 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 语法)值声明期间封装函数定义。
由于上述原因,使用没有 staticmethod
或 functools
的 "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 的解决方案。
如果您使用 aenum
1 库,您可以使用其 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
) 库的作者。
我在 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()
,但总是得到一个空列表。有什么新想法吗?
functools
、staticmethod
或包装函数。
使用 函数定义作为 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 语法)值声明期间封装函数定义。
由于上述原因,使用没有 staticmethod
或 functools
的 "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 的解决方案。
如果您使用 aenum
1 库,您可以使用其 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
) 库的作者。