关于python:如何在每次迭代中获取枚举属性的随机值?

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

我创建了这样的Enum对象:

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

我想每次都得到真正的随机值,但它不起作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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'>

我也尝试过使用lambda,但是它看起来不太好,尽管它可以工作:

1
Gender.RANDOM()

是否有其他方法可以在不使用lambda表达式的情况下每次获取随机值?

我们使用这个枚举对象作为某个方法的参数的默认值,这就是为什么它应该是一个属性而不是一个函数,因为当我们使用Gender.FEMALE时,它不是一个函数,它是一个属性,Gender.RANDOM也应该是一个属性:

1
2
3
4
5
6
def full_name(gender=Gender.FEMALE):
    ...


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


正如其他人所说,最好的方法就是让random()成为枚举类上的一个方法,以清楚地表明RANDOM不是成员。

但是,因为我喜欢拼图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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()))

在函数定义中将此值用作默认值不会得到您想要的结果。标准方法是:

1
2
3
def full_name(gender=None):
    if gender is None:
        gender = Gender.RANDOM

这会让读者感到困惑。使用普通方法更好:

1
2
3
def full_name(gender=None):
    if gender is None:
        gender = Gender.random()


我尝试了一种使用元类的方法。它工作!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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拥有RANDOM的属性。

但是,您在问题中描述的下面的代码并不像您期望的那样工作。

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

RANDOM属性只能调用一次。要知道原因,请阅读此答案。默认参数类似于函数的属性,只初始化一次。

为此,我建议你做如下的事情:

1
2
3
def full_name(gender=None):
    gender = gender or Gender.RANDOM
    ...


你可能应该在你的Enum中创建一个方法来获得一个随机的性别:

1
2
3
4
5
6
7
8
9
10
11
12
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()


由于RANDOM在您的枚举中并不是一个真正的项,所以我认为一种更为一致的方法是将它精确地保持为一个方法而不是一个属性(它毕竟不是!).

1
2
3
4
5
6
7
8
9
10
11
import random
import enum


class Gender(enum.Enum):
    MALE = 'male'
    FEMALE = 'female'

    @staticmethod
    def random():
        return random.choice(list(Gender))

然后,你可以把"我没有选择"的行为转移到函数中,它实际上更合理。

1
2
3
4
def full_name(gender=None):
    if gender is None:
        gender = Gender.random()
    # ...

我认为您需要一个随机方法,而不是直接将值保存在变量中,因为如果这样做,值将永远不会改变:

如果你不想要一个函数

随机进口导入枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()

看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   gender.RANDOM
=> 'female'
   gender.RANDOM
=> 'male'
   gender.RANDOM
=> 'male'
   gender.RANDOM
=> 'male'
   gender.RANDOM
=> 'female'
   gender.RANDOM
=> 'male'
   gender.RANDOM
=> 'male'
   gender.RANDOM
=> 'female'


尽管在许多情况下修改类可能更清晰、更干燥,但如果您只进行一次修改或不想修改枚举,则可以执行以下操作:

random.choice([enm.value for enm in Gender])


来自文档:"枚举是一组符号名(成员),绑定到唯一的常量值。"您需要像类一样切换到另一个表示。