重载子类Python枚举中的方法



来自PEP 435关于子类枚举的允许:

>>> class Foo(Enum):
...   def some_behavior(self):
...     pass
...
>>> class Bar(Foo):
...   happy = 1
...   sad = 2
...

假设我想以不同的方式为happysad枚举定义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()。但是,它可能会混淆静态类型检查器和/或检查器。

最新更新