我有一个枚举的描述符,我希望能够动态地添加到类中。这是
class TypedEnum:
'''Class to make setting of enum types valid and error checked'''
def __init__(self, enum, default=None):
self._enum = enum
self._enum_by_value = {e.value: e for e in enum}
self._enum_by_name = {e.name: e for e in enum}
self._value = next(iter(enum))
if default is not None:
self.value = default
@property
def value(self):
return self._value.value
@value.setter
def value(self, value):
if value in self._enum:
value = getattr(self._enum, value.name)
elif value in self._enum_by_name:
value = self._enum_by_name[value]
elif value in self._enum_by_value:
value = self._enum_by_value[value]
else:
raise ValueError("Value does not exist in enum: {}".format(value))
self._value = value
@property
def name(self):
return self._value.name
def __str__(self):
return repr(self._value.name)
def __get__(self, obj, type=None):
return self._value.name
def __set__(self, obj, value):
self.value = value
为了尝试理解这种行为,我编写了一个单元测试来覆盖它
from enum import Enum
from unittest import TestCase
class abc(Enum):
a = 0
b = 1
c = 2
def test_descriptor():
'''test mostly the features of __get__ and __set__
Basically when they are just objects they are fair game,
but when they become a member of a class they are super
protected (I can't even figure out a way to get access to
the base object)'''
venum = TypedEnum(abc)
assert isinstance(venum, TypedEnum)
# set works normally when not a member of an object
venum = 0
assert isinstance(venum, int)
venum = TypedEnum(abc)
x = venum
assert isinstance(x, TypedEnum)
# But when it is a member of a class/object, it is hidden
class C:
e = venum
c = C()
assert c.e == 'a' and isinstance(c.e, str)
c.e = 'b'
assert c.e == 'b' and isinstance(c.e, str)
TestCase.assertRaises(None, ValueError, setattr, c, 'e', 'z')
# However, when it is added on in init it doesn't behave the same
# way
class C:
def __init__(self):
self.e = venum
c = C()
# This would not have been true before
assert c.e != 'a' and isinstance(c.e, TypedEnum)
# to implement this, it seems we need to overload the getattribute
class D(C):
def __getattribute__(self, attr):
obj = object.__getattribute__(self, attr)
if hasattr(obj, '__get__'):
print('getting __get__', obj, attr)
return obj.__get__(self)
else:
return obj
def __setattr__(self, attr, value):
if not hasattr(self, attr):
object.__setattr__(self, attr, value)
return
obj = object.__getattribute__(self, attr)
if hasattr(obj, '__set__'):
print('setting __set__', obj, attr, value)
obj.__set__(self, value)
else:
setattr(self, attr, value)
c = D()
c.e = 'b'
assert c.e == 'b' and isinstance(c.e, str)
TestCase.assertRaises(None, ValueError, setattr, c, 'e', 'z')
test_descriptor()
这一切都是理智的吗?无论它是__dict__
还是class.__dict__
请告诉我有更好的方法来做到这一点!