我为列表创建了一个描述符。
经过测试,似乎每次我将一个值附加到一个实例的列表中时,它也会被添加到另一个实例中。
甚至更奇怪,在单元测试中它会一直附加到列表中,而不是在每次测试时重置。
我的描述符主要类:
class Field(object):
def __init__(self, type_, name, value=None, required=False):
self.type = type_
self.name = "_" + name
self.required = required
self._value = value
def __get__(self, instance, owner):
return getattr(instance, self.name, self.value)
def __set__(self, instance, value):
raise NotImplementedError
def __delete__(self, instance):
raise AttributeError("Can't delete attribute")
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value if value else self.type()
描述符列表类:
class ListField(Field):
def __init__(self, name, value_type):
super(ListField, self).__init__(list, name, value=[])
self.value_type = value_type
def __set__(self, instance, value):
if not isinstance(value, list):
raise TypeError("{} must be a list".format(self.name))
setattr(instance, self.name, value)
def __iter__(self):
for item in self.value:
yield item
def __len__(self):
return len(self.value)
def __getitem__(self, item):
return self.value[item]
def append(self, value):
if not isinstance(value, self.value_type):
raise TypeError("Value is list {} must be of type {}".format(self.name, self.value_type))
self.value.append(value)
单元测试:
# Class I created solely for testing purposes
class ListTestClass(object):
l = ListField("l", int)
class TestListFieldClass(unittest.TestCase):
def setUp(self):
self.listobject = ListTestClass()
def test_add(self):
# The first number is added to the list
self.listobject.l.append(2)
def test_multiple_instances(self):
# This test works just fine
l1 = ListField("l1", int)
l2 = ListField("l2", int)
l1.append(1)
l2.append(2)
self.assertEqual(l1[0], 1)
self.assertEqual(l2[0], 2)
def test_add_multiple(self):
# This test works just fine
l1 = ListField("l1", int)
l1.append(1)
l1.append(2)
self.assertEqual(l1[0], 1)
self.assertEqual(l1[1], 2)
def test_add_error(self):
# This test works just fine
with self.assertRaises(TypeError):
l1 = ListField("l1", int)
l1.append("1")
def test_overwrite_list(self):
# This test works just fine
l1 = ListField("l1", int)
l1 = []
l1.append(1)
def test_overwrite_error(self):
# This test works just fine
l1 = ListTestClass()
l1.l.append(1)
with self.assertRaises(TypeError):
l1.l = "foo"
def test_multiple_model_instances(self):
# I create 2 more instances of ListTestClass
l1 = ListTestClass()
l2 = ListTestClass()
l1.l.append(1)
l2.l.append(2)
self.assertEqual(l1.l[0], 1)
self.assertEqual(l2.l[0], 2)
最后一次测试失败
Failure
Traceback (most recent call last):
File "/home/user/project/tests/test_fields.py", line 211, in test_multiple_model_instances
self.assertEqual(l1.l[0], 1)
AssertionError: 2 != 1
当我查看l1.1
和l2.l
的值时,它们都有一个包含[2, 1, 2]
的列表
我在这里缺少什么?
我查看了内存地址,似乎列表都指向同一个对象。
class ListFieldTest(object):
lf1 = ListField("lf1", int)
class TestClass(object):
def __init__(self):
l1 = ListFieldTest()
l2 = ListFieldTest()
l1.lf1.append(1)
l2.lf1.append(2)
print(l1.lf1)
print(l2.lf1)
print(hex(id(l1)))
print(hex(id(l2)))
print(hex(id(l1.lf1)))
print(hex(id(l2.lf1)))
打印
[1, 2]
[1, 2]
0x7f987da018d0 --> Address for l1
0x7f987da01910 --> Address for l2
0x7f987d9c4bd8 --> Address for l1.lf1
0x7f987d9c4bd8 --> Address for l2.lf1
答案 0 :(得分:2)
ListTestClass.l
是一个类属性,因此它由类的所有实例共享。相反,您应该创建一个实例属性,例如在__init__
方法中:
class ListTestClass(object):
def __init__(self):
self.l = ListField("l", int)
类似的评论适用于ListFieldTest
。您的代码中可能存在其他类似的问题,我还没有仔细检查过它。
答案 1 :(得分:0)
根据this source,正确的形式是
class ListTestClass(object):
l_attrib = ListField("l", int)
def __init__(self)
self.l = l_attrib
答案 2 :(得分:0)
感谢@PM 2Ring和火山,我找到了答案。
最后,这对于值类型非常有用:
class IntTestClass(object):
i = IntegerField("i")
但是对于无效的参考类型(如列表),您必须添加新列表
class ListTestClass(object):
l = ListField("l", int)
def __init__(self):
self.l = []