如何编写描述符,该描述符返回具有描述符并返回其他描述符对象的描述符?在下面的代码中,getattr
中的__get__
和__set__
以不同实例作为参数被调用,但它引用了相同的对象。请告知如何通过附加测试。acces嵌套实例值的嵌套描述符
一般来说,这应该是生成严格模式的JSON报告的帮手。它会生成结构,但这些值在结构节点中很常见。只有当我在一个类中有几个相同类型的类属性(ObjectField)时才会暴露这个问题。
class Uninitialized:
pass
class FieldDescriptor(object):
def __init__(self, value_type, json_key, initial_value=Uninitialized):
self._value_type = value_type
self._storage_key = json_key
self._initial_value = initial_value
self._parent_attr_name = None
def _check_py_value(self, new_value):
if new_value is not None and not isinstance(new_value, self._value_type):
raise TypeError("Bad type %s" % type(new_value).__name__)
def _form_json_value(self, parent_instance):
return self.__get__(parent_instance)
def __get__(self, parent_instance, _=None):
value = getattr(parent_instance, self._parent_attr_name).val
return None if value is Uninitialized else value
def __set__(self, parent_instance, value):
getattr(parent_instance, self._parent_attr_name).val = value
class StrField(FieldDescriptor):
def __init__(self, json_key, initial_value=Uninitialized):
super(StrField, self).__init__(str, json_key, initial_value)
class ListField(FieldDescriptor):
def __init__(self, json_key, initial_value=Uninitialized):
super(ListField, self).__init__(list, json_key, initial_value)
class Wrap(object):
def __init__(self, val):
self.val = val
class ObjectField(FieldDescriptor):
def __init__(self, json_key):
for name_, descriptor in self.iterate_descriptors():
attr_name = "_value_of_{}".format(name_) # kind of proxy
descriptor._parent_attr_name = attr_name
new_field = Wrap(descriptor._initial_value)
setattr(self, attr_name, new_field)
FieldDescriptor.__init__(self, value_type=self.__class__, json_key=json_key, initial_value=self)
@classmethod
def iterate_descriptors(cls):
for attr_name, descriptor in cls.__dict__.iteritems():
if isinstance(descriptor, FieldDescriptor):
yield attr_name, descriptor
def _form_json_value(self, _=None):
return {dsc._storage_key: dsc._form_json_value(self) for _, dsc in self.iterate_descriptors()}
def test_it_with_pytest():
class ObjF(ObjectField):
txt = StrField("OBJF.StrDO")
list = ListField("OBJF.C")
class Nest(ObjectField):
b1 = ObjF("NEST.B1")
b2 = ObjF("NEST.B2")
class Root(ObjectField):
oo1 = Nest('oo1')
oo2 = Nest('oo2')
root = Root(None)
# assign some values
root.oo1.b1.txt = "DIFFERENT"
root.oo2.b2.list = [12, 3, 5]
assert root.oo1._value_of_b1 != root.oo2._value_of_b1 # that pass
a = root.oo1.b1.txt
b = root.oo1.b2.txt
c = root.oo2.b1.txt
assert a != b # that pass
assert a != c # that fails, 'DIFFERENT' == 'DIFFERENT'
assert root._form_json_value() == {
'oo1': {
'NEST.B1': {
'OBJF.C': None,
'OBJF.StrDO': 'DIFFERENT' # ok
},
'NEST.B2': {
'OBJF.C': None, # that fails, is [12, 3, 5]
'OBJF.StrDO': None
}
},
'oo2': {
'NEST.B1': {
'OBJF.C': None,
'OBJF.StrDO': None # that fails is "DIFFERENT"
},
'NEST.B2': {
'OBJF.C': [12, 3, 5], # ok
'OBJF.StrDO': None
}
}
}
指定'_parent_attr_name'是真正应该在元类“__init__”中发生的事情。 –
是的,它之前是这样的,但由于效果是相同的,没有使用元类(我相信),没关系。 – Mikaelblomkvistsson
它确实有效,并且对于演示文稿来说更简单,但重复的分配会让我在真实代码中感到困惑。 –