Подтвердить что ты не робот

Как получить случайное значение атрибута 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):
    ...
4b9b3361

Ответ 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])