python - ¿Cómo obtener el valor aleatorio del atributo de Enum en cada iteración?
random lambda (7)
He creado tal objeto Enum
:
class Gender(Enum):
FEMALE = ''female''
MALE = ''male''
RANDOM = random.choice([FEMALE, MALE])
y quiero obtener un valor realmente aleatorio cada vez, pero no funciona:
>>> 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''>
También he intentado usar lambda, pero no se ve tan bien, aunque funciona:
Gender.RANDOM()
¿Hay otra forma de obtener valores aleatorios cada vez, sin utilizar expresiones lambda?
Usamos este objeto enum como valor predeterminado del argumento de algún método, por eso debería ser un atributo, no una función, porque cuando usamos Gender.FEMALE
no es una función, es un atributo y Gender.RANDOM
debería ser un atributo también:
def full_name(gender=Gender.FEMALE):
...
def full_name(gender=Gender.RANDOM):
...
Aunque la modificación de la clase puede ser más clara y DRY-er en muchos casos, si solo haces esto una vez o no quieres modificar la enumeración, puedes hacerlo:
random.choice([enm.value for enm in Gender])
Como RANDOM
no es realmente un elemento en su enumeración, creo que un enfoque más coherente sería mantenerlo precisamente como un método y no como un atributo (¡no lo es después de todo!).
import random
import enum
class Gender(enum.Enum):
MALE = ''male''
FEMALE = ''female''
@staticmethod
def random():
return random.choice(list(Gender))
Luego, podría transferir el comportamiento "No estoy eligiendo" a la función en la que realmente tiene más sentido.
def full_name(gender=None):
if gender is None:
gender = Gender.random()
# ...
Como han dicho otros, la mejor manera es simplemente hacer que random()
sea un método en su clase de enumeración para dejar en claro que RANDOM
no es un miembro.
Sin embargo, como me gustan los puzzles:
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()))
Si utiliza esto como un valor predeterminado en una definición de función, no obtendrá lo que desea. El método estándar para tal es:
def full_name(gender=None):
if gender is None:
gender = Gender.RANDOM
Lo que va a ser confuso para el lector. Esto es mucho mejor usando un método normal:
def full_name(gender=None):
if gender is None:
gender = Gender.random()
Creo que quieres un método aleatorio, en lugar de guardar el valor directamente en la variable, porque si lo haces, el valor nunca cambiará:
Si no quieres una funcion
importar enum de importación aleatoria
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()
Mira:
gender.RANDOM
=> ''female''
gender.RANDOM
=> ''male''
gender.RANDOM
=> ''male''
gender.RANDOM
=> ''male''
gender.RANDOM
=> ''female''
gender.RANDOM
=> ''male''
gender.RANDOM
=> ''male''
gender.RANDOM
=> ''female''
De la documentación: "Una enumeración es un conjunto de nombres simbólicos (miembros) vinculados a valores únicos y constantes ". Necesitas cambiar a otra representación como una clase.
Probé un camino con metaclasses . ¡Y funciona!
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
Aquí, al hacer de RANDOM_ATTR
la metaclase del Gender
, Gender
es como un objeto de la clase RANDOM_ATTR
, por lo que Gender
tiene la property RANDOM
.
Sin embargo, el siguiente código que describió en su pregunta no funciona como esperaba.
def full_name(gender=Gender.RANDOM):
...
La propiedad RANDOM
será llamada una sola vez. Para saber por qué, por favor lea esta answer . Los argumentos predeterminados son como atributos para funcionar, que se inicializarán solo una vez.
Para eso te sugiero que hagas algo como esto:
def full_name(gender=None):
gender = gender or Gender.RANDOM
...
Probablemente debería crear un método en su Enum
para obtener un género aleatorio:
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()