Получение всех значений из Enum, когда значения могут быть вызваны в Python 3.7 - PullRequest
3 голосов
/ 03 мая 2020

У меня есть Enum класс в Python 3.7, определенный следующим образом:

# 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(), но всегда получаю пустой список. Есть новые идеи?

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

Если вы используете библиотеку 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, enum34 backport и Расширенное перечисление (aenum) библиотека.

1 голос
/ 03 мая 2020

Существует один способ решить эту проблему, который не был упомянут в сообщении без использования functools, staticmethod или функции-оболочки.

Проблема использования определения функции в качестве единственного значения члена Enum заключается в том, что Enum __init__ и __new__ никогда не вызываются. Имея дополнительные значения, Enum инициализируется как обычно.

Доказательство выглядит следующим образом:

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

Я не думаю, что это ошибка (как предлагается в комментариях), потому что:

Однако проблема с функциями заключается в том, что они считаются определениями методов вместо атрибутов

Что общего у нескольких решений, так это инкапсуляция определения функции во время перечисления ( синтаксис класса) объявление значения.

Из-за вышеизложенного использование «функции-обертки» без staticmethod или functools не будет работать, потому что никогда не будет вызываться. Попробуйте заменить следующее (не будет вызван ни __init__, ни __call__):

SIGMOID = sigmoid
RELU = relu

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

В заключение, , возможно, самый подход pythoni c заключает в себе определение функции в список при объявлении и развертывание в __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 (некоторые считают его использование unpythoni c вне определения класса). И сохраняет импорт и вызов functools.partial - это включает ненужные издержки вызова функции при каждом доступе члена.

Таким образом, вышеприведенный подход, возможно, может быть наиболее pythoni c решением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...