我试图在自定义类层次结构中使用子元素属性访问。
我的幻想是有效的,我可以成功地使用描述符来做到这一点。
我希望更加花哨,让课程RefHolder
(如下面的测试用例中)使用插槽来节省空间。
当我尝试使用插槽时,我得到RuntimeError: maximum recursion depth exceeded
请注意,我已经尝试过为此寻找现有的解决方案,我能找到最匹配的解决方案:
https://stackoverflow.com/a/19566973/1671693
我在下面的测试用例中尝试了这个,但我仍然得到了运行时错误。
请注意,在测试用例中,如果使用注释行而不是直接位于其下方的注释行,则会从__slots__
中删除RefHolder
,
测试用例通过。
有什么建议吗? 另外,我正在为每个属性访问创建一个对象,这似乎很昂贵,是否有任何建议以更有效的方式实现相同的行为?谢谢!
import unittest
class RefHolder():
__slots__ = ['__obj', 'get_value']
def __init__(self, obj, get_value=False):
self.__dict__['__obj'] = obj
self.__dict__['get_value']=get_value
def get_sub(self, name):
#attr = self.__dict__['__obj'].find_by_name(name)
attr = self.__dict__['__obj'].__get__(self, RefHolder).find_by_name(name)
if attr is None:
raise AttributeError("Can't find field {}".format(name))
return attr
def __getattr__(self, name):
attr = self.get_sub(name)
#if self.__dict__['get_value']:
if self.__dict__['get_value'].__get__(self, RefHolder):
return attr.Value
else:
return attr
def __setattr__(self, name, value):
attr = self.get_sub(name)
#if self.__dict__['get_value']:
if self.__dict__['get_value'].__get__(self, RefHolder):
attr.Value = value
else:
raise AttributeError("{} is read only in this context".format(name))
class ContainerAccess():
__slots__ = ['get_value']
def __init__(self, get_value=False):
self.get_value = get_value
def __get__(self, obj, objtype=None):
if obj is None:
return self
return RefHolder(obj, self.get_value)
def __set__(self, obj, value):
raise AttributeError("Read Only attribute".format(value))
class PropVal():
def __init__(self, val):
self.Value = val
@property
def Value(self):
return self._value
@Value.setter
def Value(self, value):
self._value = value
class T():
get = ContainerAccess()
getv = ContainerAccess(get_value=True)
def __init__(self):
self.store = {}
self._value = 0
def find_by_name(self, name):
return self.store.get(name)
class T2(T):
pass
class TestDesc(unittest.TestCase):
def test_it(self):
t = T()
t2 = T2()
t.store['my_val'] = PropVal(5)
t.store['my_val2'] = PropVal(6)
t2.store['my_val'] = PropVal(1)
self.assertEqual(t.get.my_val.Value, 5)
self.assertEqual(t.get.my_val2.Value, 6)
self.assertEqual(t2.get.my_val.Value, 1)
t.get.my_val.Value = 6
self.assertEqual(t.get.my_val.Value, 6)
with self.assertRaises(AttributeError):
t.get.blah.Value = 6
#self.assertEqual(t.get.my_other_val.Value, None)
self.assertEqual(t.getv.my_val, 6)
t.getv.my_val = 7
self.assertEqual(t.getv.my_val, 7)
with self.assertRaises(AttributeError):
t.get.my_val = 7