Python正在向多个实例添加值

标签 python

我已经为列表创建了一个描述符。

经过测试,似乎每次我将一个值附加到一个实例的列表时,它也会被添加到另一个实例。
更奇怪的是,在单元测试中它一直附加到列表中,而不是在每次测试时都重置。

我的描述符主类:

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.1l2.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

最佳答案

ListTestClass.l 是一个类属性,所以它被类的所有实例共享。相反,您应该创建一个实例属性,例如在 __init__ 方法中:

class ListTestClass(object):
    def __init__(self):
        self.l = ListField("l", int)

类似的说明适用于 ListFieldTest。您的代码其他地方可能还有其他类似的问题,我没有仔细检查。

关于Python正在向多个实例添加值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41719154/

相关文章:

python - Gtk.ScrolledWindow 没有水平滚动条

python - 自定义脚本的 Zabbix 发现规则 : need any advices

Python:使用 smtplib 获取 Gmail 服务器永无止境

python - def 结束时变量重置

python - Pandas 对每个类别的日期范围分别求和

python - 根据条件为 pandas df 列分配三个值

用于在一组文件中查找一组字符串实例的 Python 脚本

python - R : install matplotlib in the new Rstudio preview version

python - matplotlib 堆积面积图中的动态标签

python - 不可见的主要网格线隐藏可见的次要网格线