具有默认Enum值的Enum类方法失败 [英] Enum Class method with default Enum value fails

查看:150
本文介绍了具有默认Enum值的Enum类方法失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很清楚,如果您有一个使用枚举的类名进行类型提示的类方法,则可以通过破解来使其在Python 3.6及以下版本中正常工作。

I am well aware that if you have a class method that uses the enum's class name for type hinting, there is a hack to get it to work for Python 3.6 and below.

而不是...

class Release(Enum):
   ...
   @classmethod
   def get(cls, release: Release):
      ...

您需要像这样使用字符串值...

You need to use the string value like so...

class Release(Enum):
   ...
   @classmethod
   def get(cls, release: "Release"):
      ...

我相信在Python 3.7及更高版本中,围绕这种 hack存在一种Python方式,您无需使用引号。原因是直到所有方法和变量都先完成后该类才存在。由于该类尚不存在,因此我无法使用该类的名称,而必须使用带引号的字符串作为技巧。

I believe in Python 3.7 and above there is a pythonic way around this "hack" where you don't have to use quotes. The reason is something along the lines of "the class doesn't exist yet until all the methods and varibles are done first". Since the class doesn't exist yet, I can't use the class name yet, and have to use the quoted string as a hack.

但是,我正在尝试更进一步,并使用默认值。那是行不通的。 另外,Python 3.7及更高版本中是否有修复程序?

However, I am trying to go one step further and use a default value. And that doesn't work. Is there a pythonic approach for Python 3.6 that isn't a hack? Also, is there a fix in python 3.7 and above?

from enum import Enum

class Release(Enum):
    Canary = (1, [])
    Beta = (2, [1])
    RC = (3, [2, 1])
    Stable = (4, [3, 2, 1])

    def __new__(cls, value, cascade):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.current = ["Release" * value] # This would technically be a list of all releasese in this enum. This is just to emulate different values
        obj.cascade = cascade
        return obj

    @classmethod
    def get_all_releases(cls, release: "Release" = Canary):  # Default Value = Release.Canary
        return release.current


print(Release.get_all_releases(Release.Canary))
print(Release.get_all_releases(Release.Beta))
print(Release.get_all_releases(Release.RC))
print(Release.get_all_releases(Release.Stable))

# Error. Even with default value
# print(Release.get_all_releases())

使用此代码我收到以下错误消息

With this code I get the following error message

AttributeError: 'tuple' object has no attribute 'current'

这是因为它返回Canary的元组而不是实际值。

That is because it returns the tuple of Canary instead of the actual value.

推荐答案

您可以在发行版 枚举中进行以下操作生活更轻松,第一种是在此处所示的技术:

There are a couple things you can do in your Release Enum to make life easier, the first being a technique shown here:

    def __new__(cls, value, cascade):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.current = ["Release" * value]      # not sure what this should actually be

        # if always the previous versions (don't need cascade defined)
        obj.cascade = sorted(list(cls), reverse=True)

        # if some already defined subset (need cascade defined)
        obj.cascade = [cls._value2member_map_(c) for c in cascade]

        return obj

第二种方法可以有两种方式-您的默认值始终是第一个 Enum 成员:

The second technique can go two ways -- your default is always the first Enum member:

    @classmethod
    def get_all_releases(cls):
        return list(cls[0]).current

或者,如果默认值可以是任何成员,则类似于此答案应有效:

or, if the default could be any member, then something similar to this answer should work:

class add_default:
    """
    add DEFAULT psuedo-member to enumeration; use first member if none specified
    (default should be name of member)
    """
    def __init__(self, default=''):
        self._default = default
    def __call__(self, enumeration):
        if self._default:
            member = enumeration[self._default]
        else:
            member = enumeration[enumeration._member_names_[0]]
        enumeration._member_map_['DEFAULT'] = member
        return enumeration

您的最终 Enum 会看起来像(假设 cascade 是以前的所有成员,并使用装饰器方法):

Your final Enum would then look like (assuming cascade is all previous members and using the decorator approach):

@add_default('Canary')
class Release(Enum):
    Canary = 1
    Beta = 2
    RC = 3
    Stable = 4
    def __new__(cls, value):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.current = ["Release" * value]      # not sure what this should actually be or how it's calculated
        obj.cascade = list(cls)[::-1]
        return obj
    @classmethod
    def get_all_releases(cls, release: "Release" = None):
        if release is None:
            release = cls.DEFAULT
        return release.current

并在使用中:

>>> Release.DEFAULT
<Release.Canary: 1>

>>> Release.get_all_releases()
['Release']

>>> Release.get_all_releases(Release.RC)
['ReleaseReleaseRelease']






原始答案

您的代码存在的问题在这里:

The problem you are having with your code is here:

class Release(Enum):
    Canary = 1,

通过添加多余的逗号,您使 Canary 的值变为(1,)。删除该逗号以消除 tuple 异常。

By including that extra comma you have made the value for Canary be (1, ). Remove that comma to get rid of the tuple exception.

这篇关于具有默认Enum值的Enum类方法失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆