如何将属性和方法的访问限制为仅枚举成员?

时间:2020-06-07 08:39:49

标签: python enums properties

我打算在代码中使用非常规的Enum,但是遇到一个问题,当Enum使用不正确时,我需要我的属性来引发错误,但是,它没有抛出异常,而是输出了我财产的地址。

我希望我的代码如何工作:

  • 用户写Enum.MEMBER.text时,返回Enum.MEMBER替代文本。
  • 当用户写Enum.text时,抛出错误。

这是代码段

class MyEnum(Enum):
    @property
    def text(self):
        if isinstance(self._value_,MyCapsule):  return self._value_.text
        raise Exception('You are not using an Enum!')
        return None

    @property
    def value(self):
        if isinstance(self._value_,MyCapsule):  return self._value_.value
        raise Exception('You are not using an Enum!')
        return None

class MyCapsule:
    def __init__(self,value,text,more_data):
        self._value_, self._text_   = (value,text)
    @property
    def text(self):     return self._text_
    @property
    def value(self):    return self._value_

class CustomData(MyEnum):
    ONE = MyCapsule(1,'One','Lorem')
    TWO = MyCapsule(2,'Two','Ipsum')
    TRI = MyCapsule(3,'Tri','Loipsum')

A = CustomData.ONE
B = CustomData

print(A.text, A.value,sep=' | ')
print(B.text, B.value,sep=' | ')

输出为:

One | 1
<property object at 0x0000016CA56DF0E8> | <property object at 0x0000016CA56DF278>

我期望的是

One | 1
Unexpected exception at ....

是否有解决此问题的方法,或者我不应该以此方式写Enum

1 个答案:

答案 0 :(得分:1)

自定义描述符可以解决问题:

class property_only(object):
    #
    def __init__(self, func):
        self.func = func
    #
    def __get__(self, instance, cls):
        if instance is None:
            raise Exception('You are not using an Enum!')
        else:
            return self.func(instance)
    #
    def __set__(self, instance, value):
        # raise error or set value here
        pass

然后更改您的基本枚举以使用它:

class MyEnum(Enum):
    @property_only
    def text(self):
        return self._value_.text
    @property_only
    def value(self):
        return self._value_.value

class MyCapsule:
    def __init__(self, value, text, more_data):
        self._value_, self._text_ = (value, text)

class CustomData(MyEnum):
    ONE = MyCapsule(1, 'One', 'Lorem')
    TWO = MyCapsule(2, 'Two', 'Ipsum')
    TRI = MyCapsule(3, 'Tri', 'Loipsum')

并在使用中:

>>> CustomData.text
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __get__
Exception: You are not using an Enum!

虽然解决了“只能从枚举中访问”的问题,但是当您要访问textvalue时,仍然有很多间接方式:

>>> CustomData.ONE.value._value_
1

>>> CustomData.ONE.value._text_
'One'

解决方案是将MyCapsule直接合并到CustomData中:

from enum import Enum

class property_only(object):
    #
    def __init__(self, func):
        self.func = func
    #
    def __get__(self, instance, cls):
        if instance is None:
            raise Exception('You are not using an Enum!')
        else:
            return self.func(instance)
    #
    def __set__(self, instance, value):
        # raise error or set value here
        pass

class CustomData(Enum):
    #
    ONE = 1, 'One', 'Lorem'
    TWO = 2, 'Two', 'Ipsum'
    TRI = 3, 'Tri', 'Loipsum'
    #
    def __new__(cls, value, text, more_data):
        member = object.__new__(cls)
        member._value_ = value
        member._text_ = text
        # ignoring more_data for now...
        return member
    #
    @property_only
    def text(self):
        return self._text_
    #
    @property_only
    def value(self):
        return self._value_

并在使用中:

>>> CustomData.ONE
<CustomData.ONE: 1>

>>> CustomData.ONE.value
1

>>> CustomData.ONE.text
'One'

>>> CustomData.ONE.name
'ONE'

>>> CustomData.text
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __get__
Exception: You are not using an Enum!

>>> CustomData.value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __get__
Exception: You are not using an Enum!

披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum)库的作者。