Python 中的枚举枚举?
Enum of enums in Python?
是否可以在 Python 中使用枚举的枚举?例如,我想要
enumA
enumB
elementA
elementB
enumC
elementC
elementD
我可以将 elementA
称为 enumA.enumB.elementA
,或将 elementD
称为 enumA.enumC.elementD
。
这可能吗?如果是,怎么做?
编辑:以天真的方式实施时:
from enum import Enum
class EnumA(Enum):
class EnumB(Enum):
member = 0
print(EnumA)
print(EnumA.EnumB.member)
它给出:
<enum 'EnumA'>
Traceback (most recent call last):
File "Maps.py", line 15, in <module>
print(EnumA.EnumB.member)
AttributeError: 'EnumA' object has no attribute 'member'
您可以使用 namedtuples 来做这样的事情:
>>> from collections import namedtuple
>>> Foo = namedtuple('Foo', ['bar', 'barz'])
>>> Bar = namedtuple('Bar', ['element_a', 'element_b'])
>>> Barz = namedtuple('Barz', ['element_c', 'element_d'])
>>> bar = Bar('a', 'b')
>>> barz = Barz('c', 'd')
>>> foo = Foo(bar, barz)
>>> foo
Foo(bar=Bar(element_a='a', element_b='b'), barz=Barz(element_c='c', element_d='d'))
>>> foo.bar.element_a
'a'
>>> foo.barz.element_d
'd'
这不是枚举,但也许可以解决您的问题
您不能使用 enum
stdlib 模块执行此操作。如果您尝试一下:
class A(Enum):
class B(Enum):
a = 1
b = 2
class C(Enum):
c = 1
d = 2
A.B.a
…你只会得到一个例外:
AttributeError: 'A' object has no attribute 'a'
这是因为 A
的枚举值表现得像 A
的实例,而不是其值类型的实例。就像包含 int
值的普通枚举在值上没有 int
方法一样,B
也不会有 Enum
方法。比较:
class D(Enum):
a = 1
b = 2
D.a.bit_length()
当然,您可以显式访问基础值(int
或 B
class):
D.a.value.bit_length()
A.B.value.a
…但我怀疑这就是你想要的。
那么,您能否使用 IntEnum
使用的相同技巧,即对 Enum
和 int
进行 subclass 处理,以便其枚举值 是 int
值,如文档的 Others 部分所述?
不,因为你会选择哪种类型class?不是 Enum
;那已经是你喜欢的类型了。您不能使用 type
(任意 classes 的类型)。没有任何效果。
因此,您必须使用具有不同设计的不同 Enum 实现才能完成这项工作。幸运的是,PyPI 和 ActiveState 上大约有 69105 个不同的可供选择。
例如,当我正在考虑构建类似于 Swift 枚举的东西(比 Python/Java/etc. 枚举更接近 ML ADT)时,有人建议我看一下 makeobj
.我忘记这样做了,但现在我这样做了,并且:
class A(makeobj.Obj):
class B(makeobj.Obj):
a, b = makeobj.keys(2)
class C(makeobj.Obj):
c, d = makeobj.keys(2)
print(A.B, A.B.b, A.B.b.name, A.B.b.value)
这给你:
<Object: B -> [a:0, b:1]> <Value: B.b = 1> b 1
如果它查看它的 __qualname__
而不是它的 __name__
来创建 str/repr 值,这可能会很好,但除此之外它看起来就像你想要的一切。它还有一些其他很酷的功能(不是我想要的,但很有趣……)。
注意 下面很有趣,可能有用,但正如@abarnert 指出的结果 A
枚举没有 Enum
成员 - - 即 list(A)
return 是一个空列表。
无需评论 Enum of Enums 是否是个好主意(我还没有决定;),这是可以做到的...而且只需要少量的魔法。
您可以使用 this answer 中的 Constant
class:
class Constant:
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
或者您可以使用新的 aenum 库及其内置的 skip
描述符装饰器(这就是我将展示的内容)。
无论如何,通过将 subEnum classes 包装在描述符中,它们可以避免自己成为成员。
你的例子看起来像:
from aenum import Enum, skip
class enumA(Enum):
@skip
class enumB(Enum):
elementA = 'a'
elementB = 'b'
@skip
class enumC(Enum):
elementC = 'c'
elementD = 'd'
然后您可以访问它们:
print(enumA)
print(enumA.enumB)
print(enumA.enumC.elementD)
这给你:
<enum 'enumA'>
<enum 'enumB'>
enumC.elementD
使用 Constant
和 skip
之间的区别是深奥的:在 enumA
的 __dict__
'enumB'
中 return 一个 Constant
对象(如果使用 Constant
)或 <enum 'enumB'>
如果使用 skip
;正常访问总是 return <enum 'enumB'>
.
在 Python 3.5+ 中,您甚至可以(取消)pickle 嵌套枚举:
print(pickle.loads(pickle.dumps(enumA.enumC.elementD)) is enumA.enumC.elementD)
# True
请注意,subEnum 在其显示中不包含父 Enum;如果这很重要,我会建议增强 EnumMeta
以识别 Constant
描述符并修改其包含的 class' __repr__
-- 但我将把它留作 reader。 ;)
我在基础枚举中创建了一个枚举实现 de __ getattr __ 的枚举
def __getattr__(self, item):
if item != '_value_':
return getattr(self.value, item).value
raise AttributeError
在我的例子中,我有一个枚举的枚举
class enumBase(Enum):
class innerEnum(Enum):
class innerInnerEnum(Enum):
A
和
enumBase.innerEnum.innerInnerEnum.A
有效
基于属性的解决方案。这也允许实现属性验证器和 attrs 的其他优点:
import enum
import attr
class CoilsTypes(enum.Enum):
heating: str = "heating"
class FansTypes(enum.Enum):
plug: str = "plug"
class HrsTypes(enum.Enum):
plate: str = "plate"
rotory_wheel: str = "rotory wheel"
class FiltersTypes(enum.Enum):
bag: str = "bag"
pleated: str = "pleated"
@attr.dataclass(frozen=True)
class ComponentTypes:
coils: CoilsTypes = CoilsTypes
fans: FansTypes = FansTypes
hrs: HrsTypes = HrsTypes
filter: FiltersTypes = FiltersTypes
cmp = ComponentTypes()
res = cmp.hrs.plate
如果你不关心继承,这是我以前用过的解决方案:
class Animal:
class Cat(enum.Enum):
TIGER = "TIGER"
CHEETAH = "CHEETAH"
LION = "LION"
class Dog(enum.Enum):
WOLF = "WOLF"
FOX = "FOX"
def __new__(cls, name):
for member in cls.__dict__.values():
if isinstance(member, enum.EnumMeta) and name in member.__members__:
return member(name)
raise ValueError(f"'{name}' is not a valid {cls.__name__}")
它通过覆盖 Animal
的 __new__
方法来找到合适的子枚举和 return 它的一个实例。
用法:
Animal.Dog.WOLF #=> <Dog.WOLF: 'WOLF'>
Animal("WOLF") #=> <Dog.WOLF: 'WOLF'>
Animal("WOLF") is Animal.Dog.WOLF #=> True
Animal("WOLF") is Animal.Dog.FOX #=> False
Animal("WOLF") in Animal.Dog #=> True
Animal("WOLF") in Animal.Cat #=> False
Animal("OWL") #=> ValueError: 'OWL' is not a valid Animal
然而,值得注意的是:
isinstance(Animal.Dog, Animal) #=> False
只要你不关心这个解决方案就可以很好地工作。不幸的是,似乎没有办法在内部 class 的定义中引用外部 class,因此没有简单的方法使 Dog
扩展 Animal
.
是否可以在 Python 中使用枚举的枚举?例如,我想要
enumA
enumB
elementA
elementB
enumC
elementC
elementD
我可以将 elementA
称为 enumA.enumB.elementA
,或将 elementD
称为 enumA.enumC.elementD
。
这可能吗?如果是,怎么做?
编辑:以天真的方式实施时:
from enum import Enum
class EnumA(Enum):
class EnumB(Enum):
member = 0
print(EnumA)
print(EnumA.EnumB.member)
它给出:
<enum 'EnumA'>
Traceback (most recent call last):
File "Maps.py", line 15, in <module>
print(EnumA.EnumB.member)
AttributeError: 'EnumA' object has no attribute 'member'
您可以使用 namedtuples 来做这样的事情:
>>> from collections import namedtuple
>>> Foo = namedtuple('Foo', ['bar', 'barz'])
>>> Bar = namedtuple('Bar', ['element_a', 'element_b'])
>>> Barz = namedtuple('Barz', ['element_c', 'element_d'])
>>> bar = Bar('a', 'b')
>>> barz = Barz('c', 'd')
>>> foo = Foo(bar, barz)
>>> foo
Foo(bar=Bar(element_a='a', element_b='b'), barz=Barz(element_c='c', element_d='d'))
>>> foo.bar.element_a
'a'
>>> foo.barz.element_d
'd'
这不是枚举,但也许可以解决您的问题
您不能使用 enum
stdlib 模块执行此操作。如果您尝试一下:
class A(Enum):
class B(Enum):
a = 1
b = 2
class C(Enum):
c = 1
d = 2
A.B.a
…你只会得到一个例外:
AttributeError: 'A' object has no attribute 'a'
这是因为 A
的枚举值表现得像 A
的实例,而不是其值类型的实例。就像包含 int
值的普通枚举在值上没有 int
方法一样,B
也不会有 Enum
方法。比较:
class D(Enum):
a = 1
b = 2
D.a.bit_length()
当然,您可以显式访问基础值(int
或 B
class):
D.a.value.bit_length()
A.B.value.a
…但我怀疑这就是你想要的。
那么,您能否使用 IntEnum
使用的相同技巧,即对 Enum
和 int
进行 subclass 处理,以便其枚举值 是 int
值,如文档的 Others 部分所述?
不,因为你会选择哪种类型class?不是 Enum
;那已经是你喜欢的类型了。您不能使用 type
(任意 classes 的类型)。没有任何效果。
因此,您必须使用具有不同设计的不同 Enum 实现才能完成这项工作。幸运的是,PyPI 和 ActiveState 上大约有 69105 个不同的可供选择。
例如,当我正在考虑构建类似于 Swift 枚举的东西(比 Python/Java/etc. 枚举更接近 ML ADT)时,有人建议我看一下 makeobj
.我忘记这样做了,但现在我这样做了,并且:
class A(makeobj.Obj):
class B(makeobj.Obj):
a, b = makeobj.keys(2)
class C(makeobj.Obj):
c, d = makeobj.keys(2)
print(A.B, A.B.b, A.B.b.name, A.B.b.value)
这给你:
<Object: B -> [a:0, b:1]> <Value: B.b = 1> b 1
如果它查看它的 __qualname__
而不是它的 __name__
来创建 str/repr 值,这可能会很好,但除此之外它看起来就像你想要的一切。它还有一些其他很酷的功能(不是我想要的,但很有趣……)。
注意 下面很有趣,可能有用,但正如@abarnert 指出的结果 A
枚举没有 Enum
成员 - - 即 list(A)
return 是一个空列表。
无需评论 Enum of Enums 是否是个好主意(我还没有决定;),这是可以做到的...而且只需要少量的魔法。
您可以使用 this answer 中的 Constant
class:
class Constant:
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
或者您可以使用新的 aenum 库及其内置的 skip
描述符装饰器(这就是我将展示的内容)。
无论如何,通过将 subEnum classes 包装在描述符中,它们可以避免自己成为成员。
你的例子看起来像:
from aenum import Enum, skip
class enumA(Enum):
@skip
class enumB(Enum):
elementA = 'a'
elementB = 'b'
@skip
class enumC(Enum):
elementC = 'c'
elementD = 'd'
然后您可以访问它们:
print(enumA)
print(enumA.enumB)
print(enumA.enumC.elementD)
这给你:
<enum 'enumA'>
<enum 'enumB'>
enumC.elementD
使用 Constant
和 skip
之间的区别是深奥的:在 enumA
的 __dict__
'enumB'
中 return 一个 Constant
对象(如果使用 Constant
)或 <enum 'enumB'>
如果使用 skip
;正常访问总是 return <enum 'enumB'>
.
在 Python 3.5+ 中,您甚至可以(取消)pickle 嵌套枚举:
print(pickle.loads(pickle.dumps(enumA.enumC.elementD)) is enumA.enumC.elementD)
# True
请注意,subEnum 在其显示中不包含父 Enum;如果这很重要,我会建议增强 EnumMeta
以识别 Constant
描述符并修改其包含的 class' __repr__
-- 但我将把它留作 reader。 ;)
我在基础枚举中创建了一个枚举实现 de __ getattr __ 的枚举
def __getattr__(self, item):
if item != '_value_':
return getattr(self.value, item).value
raise AttributeError
在我的例子中,我有一个枚举的枚举
class enumBase(Enum):
class innerEnum(Enum):
class innerInnerEnum(Enum):
A
和
enumBase.innerEnum.innerInnerEnum.A
有效
基于属性的解决方案。这也允许实现属性验证器和 attrs 的其他优点:
import enum
import attr
class CoilsTypes(enum.Enum):
heating: str = "heating"
class FansTypes(enum.Enum):
plug: str = "plug"
class HrsTypes(enum.Enum):
plate: str = "plate"
rotory_wheel: str = "rotory wheel"
class FiltersTypes(enum.Enum):
bag: str = "bag"
pleated: str = "pleated"
@attr.dataclass(frozen=True)
class ComponentTypes:
coils: CoilsTypes = CoilsTypes
fans: FansTypes = FansTypes
hrs: HrsTypes = HrsTypes
filter: FiltersTypes = FiltersTypes
cmp = ComponentTypes()
res = cmp.hrs.plate
如果你不关心继承,这是我以前用过的解决方案:
class Animal:
class Cat(enum.Enum):
TIGER = "TIGER"
CHEETAH = "CHEETAH"
LION = "LION"
class Dog(enum.Enum):
WOLF = "WOLF"
FOX = "FOX"
def __new__(cls, name):
for member in cls.__dict__.values():
if isinstance(member, enum.EnumMeta) and name in member.__members__:
return member(name)
raise ValueError(f"'{name}' is not a valid {cls.__name__}")
它通过覆盖 Animal
的 __new__
方法来找到合适的子枚举和 return 它的一个实例。
用法:
Animal.Dog.WOLF #=> <Dog.WOLF: 'WOLF'>
Animal("WOLF") #=> <Dog.WOLF: 'WOLF'>
Animal("WOLF") is Animal.Dog.WOLF #=> True
Animal("WOLF") is Animal.Dog.FOX #=> False
Animal("WOLF") in Animal.Dog #=> True
Animal("WOLF") in Animal.Cat #=> False
Animal("OWL") #=> ValueError: 'OWL' is not a valid Animal
然而,值得注意的是:
isinstance(Animal.Dog, Animal) #=> False
只要你不关心这个解决方案就可以很好地工作。不幸的是,似乎没有办法在内部 class 的定义中引用外部 class,因此没有简单的方法使 Dog
扩展 Animal
.