使用默认枚举值的枚举 Class 方法失败
Enum Class method with default Enum value fails
我很清楚,如果你有一个 class 方法,它使用枚举的 class 名称来进行类型提示,那么有一个 hack 可以让它在 Python 3.6 上工作及以下。
而不是...
class Release(Enum):
...
@classmethod
def get(cls, release: Release):
...
您需要像这样使用字符串值...
class Release(Enum):
...
@classmethod
def get(cls, release: "Release"):
...
我相信 Python 3.7 及更高版本有一个 pythonic 方法可以绕过这个 "hack",您不必使用引号。原因与 "the class doesn't exist yet until all the methods and varibles are done first" 类似。由于 class 还不存在,我还不能使用 class 名称,必须使用引号引起来的字符串。
但是,我试图更进一步并使用默认值。那是行不通的。 python3.6 的 pythonic 方法不是 hack 吗? 此外,python 3.7 及更高版本是否有修复程序?
代码
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())
使用此代码,我收到以下错误消息
AttributeError: 'tuple' object has no attribute 'current'
那是因为它returns金丝雀的元组而不是实际值
虽然这绝对是一种解决方法,但它似乎对我很有效:
@classmethod
def get_all_releases(cls, release: "Release" = Canary): # Default Value = Release.Canary
if release == (Release.Canary.value,):
return Release.Canary.current
return release.current
它对您分配给 Canary
的任何值都有效。因此,只要这是您的默认设置,我相信它就会起作用。
为了更通用,您只需调整 class 定义中的默认值而不是每个函数,您可以按如下方式进行:
class Release(Enum):
Canary = 6,
Beta = 2,
RC = 3,
Stable = 4
default = Canary
...
@classmethod
def get_all_releases(cls, release: "Release" = default):
if release == (Release.Canary.value,):
return Release.Canary.current
return release.current
您可以在 Release
Enum
中做一些事情来让生活更轻松,第一个是展示的技巧 :
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
成员:
@classmethod
def get_all_releases(cls):
return list(cls[0]).current
或者,如果默认值可以是任何成员,那么类似于 的东西应该可以工作:
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
是所有以前的成员并使用装饰器方法):
@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']
原答案
您的代码遇到的问题在这里:
class Release(Enum):
Canary = 1,
通过包含额外的逗号,您已将 Canary
的值设为 (1, )
。删除该逗号以摆脱 tuple
异常。
从@ufoxDan 的回答中得到了提示,但试图减少变通方法,使其更自然。
基本上,我在 return
ing 之前先检查 type(release)
并注意到我得到了..
的结果
<enum 'Release'>
<enum 'Release'>
<enum 'Release'>
<enum 'Release'>
<class 'tuple'>
我注意到如果类型是 Release
那么我可以只执行代码,但是如果它是 anything 其他的,比如 None
而不是未创建的 Canary
类型,那么我可以假设它要求 Canary
。所以我做了以下...
@classmethod
def get_all_releases(cls, release: "Release" = None):
if type(release) is Release:
return release.current
return Release.Canary.current
# Now these all work
print(Release.get_all_releases())
print(Release.get_all_releases(Release.Canary))
print(Release.get_all_releases(Release.Stable))
这似乎是实现结果的最 pythonic 方式。这似乎也是阅读代码且不重复代码的最佳方式。任何人都应该能够实现类似的东西。
我很清楚,如果你有一个 class 方法,它使用枚举的 class 名称来进行类型提示,那么有一个 hack 可以让它在 Python 3.6 上工作及以下。
而不是...
class Release(Enum):
...
@classmethod
def get(cls, release: Release):
...
您需要像这样使用字符串值...
class Release(Enum):
...
@classmethod
def get(cls, release: "Release"):
...
我相信 Python 3.7 及更高版本有一个 pythonic 方法可以绕过这个 "hack",您不必使用引号。原因与 "the class doesn't exist yet until all the methods and varibles are done first" 类似。由于 class 还不存在,我还不能使用 class 名称,必须使用引号引起来的字符串。
但是,我试图更进一步并使用默认值。那是行不通的。 python3.6 的 pythonic 方法不是 hack 吗? 此外,python 3.7 及更高版本是否有修复程序?
代码
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())
使用此代码,我收到以下错误消息
AttributeError: 'tuple' object has no attribute 'current'
那是因为它returns金丝雀的元组而不是实际值
虽然这绝对是一种解决方法,但它似乎对我很有效:
@classmethod
def get_all_releases(cls, release: "Release" = Canary): # Default Value = Release.Canary
if release == (Release.Canary.value,):
return Release.Canary.current
return release.current
它对您分配给 Canary
的任何值都有效。因此,只要这是您的默认设置,我相信它就会起作用。
为了更通用,您只需调整 class 定义中的默认值而不是每个函数,您可以按如下方式进行:
class Release(Enum):
Canary = 6,
Beta = 2,
RC = 3,
Stable = 4
default = Canary
...
@classmethod
def get_all_releases(cls, release: "Release" = default):
if release == (Release.Canary.value,):
return Release.Canary.current
return release.current
您可以在 Release
Enum
中做一些事情来让生活更轻松,第一个是展示的技巧
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
成员:
@classmethod
def get_all_releases(cls):
return list(cls[0]).current
或者,如果默认值可以是任何成员,那么类似于
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
是所有以前的成员并使用装饰器方法):
@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']
原答案
您的代码遇到的问题在这里:
class Release(Enum):
Canary = 1,
通过包含额外的逗号,您已将 Canary
的值设为 (1, )
。删除该逗号以摆脱 tuple
异常。
从@ufoxDan 的回答中得到了提示,但试图减少变通方法,使其更自然。
基本上,我在 return
ing 之前先检查 type(release)
并注意到我得到了..
<enum 'Release'>
<enum 'Release'>
<enum 'Release'>
<enum 'Release'>
<class 'tuple'>
我注意到如果类型是 Release
那么我可以只执行代码,但是如果它是 anything 其他的,比如 None
而不是未创建的 Canary
类型,那么我可以假设它要求 Canary
。所以我做了以下...
@classmethod
def get_all_releases(cls, release: "Release" = None):
if type(release) is Release:
return release.current
return Release.Canary.current
# Now these all work
print(Release.get_all_releases())
print(Release.get_all_releases(Release.Canary))
print(Release.get_all_releases(Release.Stable))
这似乎是实现结果的最 pythonic 方式。这似乎也是阅读代码且不重复代码的最佳方式。任何人都应该能够实现类似的东西。