覆盖子类化Python枚举中的方法

时间:2015-09-27 02:22:57

标签: python python-3.x enums

在子类化枚举上从PEP 435开始,允许以下内容:

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

我想以some_behaviorhappy枚举的不同方式定义sad

有没有更好的方法来做到这一点:

>>> 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

对我来说这看起来很笨拙。

2 个答案:

答案 0 :(得分:3)

不,没有。

我的意思是,你可能会做这样的事情:

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()等表达式。但是,它可能会混淆静态类型的检查器和/或短路。

答案 1 :(得分:0)

是的,有 1

诀窍在于覆盖__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 Enumenum34 backportAdvanced Enumeration (aenum)库的作者。