Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get random value of attribute of Enum on each iteration?

I have created such Enum object:

class Gender(Enum):
    FEMALE = 'female'
    MALE = 'male'
    RANDOM = random.choice([FEMALE, MALE])

and i want to get really random value each time, but it does not work:

>>> 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'>

I have also tried use lambda, but it's looks not so good, although it works:

Gender.RANDOM()

Are there other way to get random values each time, without using lambda expressions?

We use this enum object as default value of the argument of the some method that's why it should be an attribute, not a function, because when we use Gender.FEMALE it is not a function, it's an attribute and Gender.RANDOM should be an attribute too:

def full_name(gender=Gender.FEMALE):
    ...


def full_name(gender=Gender.RANDOM):
    ...
like image 724
Lo L Avatar asked Nov 17 '17 14:11

Lo L


People also ask

How do you generate a random value from an enum?

Inside randomDirection(), we call the method nextInt() with an integer argument. The nextInt() method returns a random number to access the directions array; therefore, we need to make sure the integer is not out of the bounds of the array by passing a bound argument to nextInt().

What does enum Auto () do?

Summary. Use enum auto() class to generate unique values for enumeration members.

How do you generate a random enum in Java?

This method use the java. util. Random to create a random value. This random value then will be used to pick a random value from the enum.

Can enums have the same value python?

By definition, the enumeration member values are unique. However, you can create different member names with the same values.


3 Answers

As others have said, the best way is to just make random() be a method on your enum class to make it clear that RANDOM is not a member.

However, since I like 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()))

In your full_name definition, using Gender.RANDOM as a default value will not get you what you want. The standard for such is:

def full_name(gender=None):
    if gender is None:
        gender = Gender.RANDOM   # we get `MALE` or `FEMALE`, not `RANDOM`

Which is going to be confusing to the reader. This is much better using a normal method:

def full_name(gender=None):
    if gender is None:
        gender = Gender.random()
like image 172
Ethan Furman Avatar answered Oct 12 '22 00:10

Ethan Furman


I tried a way with metaclasses. And it works!

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

Here by making RANDOM_ATTR the metaclass of Gender, Gender is like an object of class RANDOM_ATTR, so Gender has the property RANDOM.

However,the below code you described in your question doesn't work the way you expect.

def full_name(gender=Gender.RANDOM):
    ...

The RANDOM property will be called only once. To know why, please read this answer. Default arguments are like attributes to function, which will be initialised only once.

For that i would suggest you do something like this:

def full_name(gender=None):
    gender = gender or Gender.RANDOM
    ...
like image 38
Abhijith Asokan Avatar answered Oct 12 '22 00:10

Abhijith Asokan


You probably should create a method in your Enum to obtain a random gender:

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()
like image 23
Reblochon Masque Avatar answered Oct 12 '22 01:10

Reblochon Masque