Как получить случайное значение атрибута Enum на каждой итерации?
Я создал такой объект Enum
:
class Gender(Enum):
FEMALE = 'female'
MALE = 'male'
RANDOM = random.choice([FEMALE, MALE])
и я хочу получать действительно случайное значение каждый раз, но он не работает:
>>> class Gender(Enum):
... MALE = 'male'
... FEMALE = 'female'
... RANDOM = choice([MALE, FEMALE])
...
>>> Gender.RANDOM
<Gender.MALE: 'male'>
>>> Gender.RANDOM
<Gender.MALE: 'male'>
>>> Gender.RANDOM
<Gender.MALE: 'male'>
>>> Gender.RANDOM
<Gender.MALE: 'male'>
Я также пробовал использовать лямбда, но он выглядит не очень хорошо, хотя он работает:
Gender.RANDOM()
Есть ли другой способ получить случайные значения каждый раз, не используя лямбда-выражения?
Мы используем этот объект enum как значение по умолчанию для аргумента некоторого метода, поэтому он должен быть атрибутом, а не функцией, потому что, когда мы используем Gender.FEMALE
, это не функция, это атрибут и Gender.RANDOM
также должен быть атрибутом:
def full_name(gender=Gender.FEMALE):
...
def full_name(gender=Gender.RANDOM):
...
Ответы
Ответ 1
Я пробовал использовать метаклассы. И это работает!
import random
import enum
class RANDOM_ATTR(enum.EnumMeta):
@property
def RANDOM(self):
return random.choice([Gender.MALE, Gender.FEMALE])
class Gender(enum.Enum,metaclass=RANDOM_ATTR): #this syntax works for python3 only
FEMALE = 'female'
MALE = 'male'
print(Gender.RANDOM) #prints male or female randomly
Здесь, делая RANDOM_ATTR
, метакласс из Gender
, Gender
подобен объекту класса RANDOM_ATTR
, поэтому Gender
имеет property RANDOM
.
Однако приведенный ниже код, описанный в вашем вопросе, не работает так, как вы ожидаете.
def full_name(gender=Gender.RANDOM):
...
Свойство RANDOM
будет вызываться только один раз. Чтобы узнать, почему, прочитайте этот ответ. Аргументы по умолчанию похожи на атрибуты, которые будут инициализированы только один раз.
Для этого я предлагаю вам сделать что-то вроде этого:
def full_name(gender=None):
gender = gender or Gender.RANDOM
...
Ответ 2
Как уже говорили другие, лучший способ - просто сделать random()
методом для вашего класса enum, чтобы было ясно, что RANDOM
не является членом.
Тем не менее, так как я люблю головоломки:
from enum import Enum
import random
class enumproperty(object):
"like property, but on an enum class"
def __init__(self, fget):
self.fget = fget
def __get__(self, instance, ownerclass=None):
if ownerclass is None:
ownerclass = instance.__class__
return self.fget(ownerclass)
def __set__(self, instance, value):
raise AttributeError("can't set pseudo-member %r" % self.name)
def __delete__(self, instance):
raise AttributeError("can't delete pseudo-member %r" % self.name)
class Gender(Enum):
FEMALE = 'female'
MALE = 'male'
@enumproperty
def RANDOM(cls):
return random.choice(list(cls.__members__.values()))
В вашем full_name
определении имени использование Gender.RANDOM
в качестве значения по умолчанию не даст вам того, что вы хотите. Стандарт для таких:
def full_name(gender=None):
if gender is None:
gender = Gender.RANDOM # we get 'MALE' or 'FEMALE', not 'RANDOM'
Что будет смущать читателя. Это намного лучше, используя обычный метод:
def full_name(gender=None):
if gender is None:
gender = Gender.random()
Ответ 3
Вероятно, вы должны создать метод в Enum
для получения случайного поля:
import random
import enum
class Gender(enum.Enum):
FEMALE = 'female'
MALE = 'male'
@classmethod
def get_gender(cls):
return random.choice([Gender.FEMALE, Gender.MALE])
Gender.get_gender()
Ответ 4
Я думаю, что вам нужен случайный случай, вместо этого нужно сохранить значение непосредственно в переменной, потому что если вы это сделаете, значение никогда не изменится:
Если вы НЕ ХОТИТЕ ФУНКЦИЮ
импорт случайных
import enum
class Gender(enum.Enum):
MALE = 'male'
FEMALE = 'female'
RANDOM = random.choice([MALE, FEMALE])
def __getattribute__(self,item):
if item == "RANDOM":
Gender.RANDOM = random.choice([self.MALE, self.FEMALE])
return Gender.RANDOM
else:
return object.__getattribute__(self, item)
gender = Gender()
внешний вид:
gender.RANDOM
=> 'female'
gender.RANDOM
=> 'male'
gender.RANDOM
=> 'male'
gender.RANDOM
=> 'male'
gender.RANDOM
=> 'female'
gender.RANDOM
=> 'male'
gender.RANDOM
=> 'male'
gender.RANDOM
=> 'female'
Ответ 5
Поскольку RANDOM
на самом деле не является элементом в вашем перечислении, я думаю, что более последовательный подход состоял бы в том, чтобы сохранить его точно как метод, а не атрибут (это еще не все!).
import random
import enum
class Gender(enum.Enum):
MALE = 'male'
FEMALE = 'female'
@staticmethod
def random():
return random.choice(list(Gender))
Затем вы можете перенести поведение "Я не выбираю" в функцию, где это на самом деле имеет больше смысла.
def full_name(gender=None):
if gender is None:
gender = Gender.random()
# ...
Ответ 6
Из документации: "Перечисление представляет собой набор символических имен (членов), привязанных к уникальным значениям константа".
Вам нужно переключиться на другое представление, подобное классу.
Ответ 7
Хотя модификация класса может быть более понятной и DRY-er во многих случаях, если вы делаете это только один раз или не хотите изменять перечисление, которое вы могли бы сделать:
random.choice([enm.value for enm in Gender])