来自PEP 435关于子类枚举的允许:
>>> class Foo(Enum):
... def some_behavior(self):
... pass
...
>>> class Bar(Foo):
... happy = 1
... sad = 2
...
假设我想以不同的方式为happy
和sad
枚举定义some_behavior
。
有没有比这样做更好的方法呢:
>>> class Bar(Foo):
... happy = 1
... sad = 2
... def some_behavior(self):
... if self is Bar.happy:
... # happy behavior
... elif self is Bar.sad:
... # sad behavior
那看起来很笨重。
是的,有<一口> 1> p>技巧在于重写__getattribute__
,它拦截所有名称查找,并且是非常危险的2:一口>
class Foo(Enum):
def __getattribute__(self, name):
# overriding this method is dangerous!
#
# enum member value must be an instance of a class
value_dict = super().__getattribute__('_value_').__class__.__dict__
if name in value_dict:
# bind the enum member instance to the method and return it
return partial(value_dict[name], self)
else:
# otherwise return the found object unchanged
return super().__getattribute__(name)
def __repr__(self):
# clean up the repr()
return '<%s.%s>' % (self.__class__.__name__, self.name)
添加一个小的辅助函数:
def member(cls):
# convert the class into an instance of itself
return cls()
然后写最终的Enum
:
class Bar(Foo):
#
# default methods
#
def some_behavior(self):
return self.name + ' is neutral'
def likes_to(self):
return 'likes to sit'
#
# members
#
@member
class happy:
# overridden methods
def some_behavior(self):
return self.name + ' is happy'
def likes_to(self):
return 'likes to dance'
@member
class sad:
# overridden method
def some_behavior(self):
return self.name + ' is sad'
@member
class okay:
# uses default methods
pass
使用中:
>>> list(Bar)
[<Bar.happy>, <Bar.sad>, <Bar.okay>]
>>> Bar.happy.some_behavior()
'happy is happy'
>>> Bar.happy.likes_to()
'likes to dance'
>>> Bar.sad.some_behavior()
'sad is sad'
>>> Bar.sad.likes_to()
'likes to sit'
>>> Bar.okay.some_behavior()
'okay is neutral'
>>> Bar.okay.likes_to()
'likes to sit'
1绝对不习惯。
2重写__getattribute__
是危险的,因为它控制了如何处理属性——例如,描述符魔术在object.__getattribute__
中实现。这里的任何错误都可能导致难以调试的问题
披露:我是Python stdlib Enum
, enum34
后端口和高级枚举(aenum
)库的作者。
没有。
我的意思是,你可以这样做:
def some_behavior(self):
return {Bar.happy: some_function
Bar.sad: some_other_function}[self](arguments?)
或者像这样:
def some_behavior(self):
custom_thing = {Bar.happy: some_function
Bar.sad: some_other_function}[self]
# do something which is the same for both
custom_thing()
# do something else the same for both
但是除非some_function
等已经存在,这可能不会比你现在拥有的更好(尽管你可能能够节省一两个级别的缩进,我想)。您可以在这里使用lambdas,但这很快就会变得很难看,我不推荐它,除非在最简单的情况下(通常可以用functools.partial
处理)。
如评论中所讨论的,可以做这样的事情:
class Foo(Enum):
happy = 1
sad = 2
def happy_behavior(): # No self argument!
self = Foo.happy # only if you need self
...
def sad_behavior():
self = Foo.sad
...
Foo.happy.some_behavior = happy_behavior
Foo.sad.some_behavior = sad_behavior
这是相当丑陋的,在我看来,但它应该在所有合理的情况下工作,包括表达式像Foo(1).some_behavior()
或Foo['sad'].some_behavior()
。但是,它可能会混淆静态类型检查器和/或检查器。