【问题标题】:How to get random value of attribute of Enum on each iteration?如何在每次迭代中获取 Enum 属性的随机值?
【发布时间】:2018-05-01 09:38:58
【问题描述】:

我已经创建了这样的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'>

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

Gender.RANDOM()

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

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

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


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

【问题讨论】:

  • 默认属性只计算一次(定义函数时),所以随机值只会随机一次,每次调用该函数都会得到相同的初始值。
  • 这是我想改变的常见行为。
  • 这是 Python 的基本行为,您无法更改。
  • 我指的是解决方法。当然,我无法更改 Python,我也不想这样做。

标签: python random lambda enums


【解决方案1】:

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

如果您不想要函数

随机导入 导入枚举

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'

【讨论】:

  • 我试过了,它的工作原理,但大写的方法看起来很难看。
  • @LkRi 我知道它看起来很难看.. 我只是按照你的例子,显然你可以输入你想要的名字
  • @LkRi now,你看我改了,即使你每次调用 random 方法,你都会把它存储在 RANDOM 变量中
  • 使用classmethod 你的RANDOM 签名应该是def RANDOM(cls): and your line can me RANDOM = random.choice(list(cls))`
  • @LkRi 现在我明白你的问题了
【解决方案2】:

您可能应该在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()

【讨论】:

  • 我们使用这个枚举对象作为一些方法的默认值,它应该是Gender.RANDOM,而不是Gender.get_random()Gender.RANDOM()。这是为了清晰的API。
  • 是的,classmethod是我的本意,谢谢指出
【解决方案3】:

来自文档:“枚举是一组符号名称(成员),绑定到唯一的常量值。” 您需要切换到另一种表示形式,例如类。

【讨论】:

  • 为什么?保留Enum 有其优势,尤其是使用Gender 的类型
  • 好吧,我认为pythonic的方式只是创建一个在枚举之外返回随机性别的函数......
  • 使用Enum 很好。不使用 RANDOM 作为非常数。
【解决方案4】:

我尝试了metaclasses 的方法。它有效!

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 属性只会被调用一次。要了解原因,请阅读此answer。默认参数就像函数的属性,只会被初始化一次。

为此,我建议您这样做:

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

【讨论】:

  • 虽然它确实有效,但我不赞成,因为 EnumMeta 只应在必要时进行子类化,并且还有其他方法可以解决此问题。有关EnumMeta 子类化的详细信息,请参阅When should I subclass EnumMeta。但是,由于这是一项成功的努力,因此我提供了赏金。
【解决方案5】:

尽管在许多情况下修改类可能会更清晰和 DRY-er,但如果您只这样做一次或不想修改枚举,您可以这样做:

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

【讨论】:

    【解决方案6】:

    由于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()
        # ...
    

    【讨论】:

      【解决方案7】:

      正如其他人所说,最好的方法是让 random() 成为枚举类上的一个方法,以明确 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()
      

      【讨论】:

      • 你是对的。这比使用 Gender.RANDOM 作为默认值要清楚得多。谢谢!
      【解决方案8】:

      另一个适合您目标的选项是:

      from enum import Enum
      import random
      
      class Gender(Enum):
          MALE = object()
          FEMALE = object()
      
          @classmethod
          def get_random_gender(cls):
              gender = random.choice(cls._member_names_)
              return gender.lower()
      
      
      print(Gender.get_random_gender())
      >> female
      

      【讨论】:

        猜你喜欢
        • 2020-02-21
        • 1970-01-01
        • 2018-04-04
        • 2022-07-08
        • 1970-01-01
        • 2021-12-04
        • 2012-03-04
        • 1970-01-01
        相关资源
        最近更新 更多