2017-01-18 5 views
3

私はリストの記述子を作成しました。それをテストした後Pythonは複数のインスタンスに値を追加しています

は、私は1つのインスタンスのリストに値を追加するたびに、それは同様に別のインスタンスに追加されているようです。
ユニットテストでは、リストに追加されたままであり、すべてのテストでリセットされることはありません。

私の記述メインクラス:

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 
+0

を追加する必要はありません(リストのような)参照型のためしかし

class IntTestClass(object): i = IntegerField("i") 

なぜ時々 'self.value'と時には' self._value'ですか? –

+0

@JohnZwinck self._valueは、valueプロパティの値を格納するためにのみ使用されます。 _valueはgetterとsetterの値 –

+0

でのみ使用されています。記述子プロトコルを覚えている限り、記述子ベースの属性をクラス変数として割り当てることになっています。そうでなければ、正しく動作しません。http://すべてがさえ、オブジェクトであり、ボンネットの下にそれがポインタを介して操作ですので、Pythonでnbviewer.jupyter.org/urls/gist.github.com/ChrisBeaumont/5758381/raw/descriptor_writeup.ipynb – volcano

答えて

2

ListTestClass.lを印刷します。代わりに、あなたは__init__方法では例えば、インスタンスの属性を作成する必要があります。

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

同様の発言はListFieldTestに適用されます。あなたのコードのどこかに他の同様の問題があるかもしれませんが、私はそれを詳しく調べていません。

0

this sourceによると、適切なフォームは@PM 2Ringと私は答えを見つけた火山の両方に

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

おかげです。最後に

これは値型のための素晴らしい作品:仕事とあなたが新しいリストに

class ListTestClass(object): 
    l = ListField("l", int) 

    def __init__(self): 
     self.l = [] 
+2

_Everything_は、「参照型」であります単純な整数。しかし、いくつかのオブジェクト(例:整数、文字列、タプル)は不変なので、安全に共有することができますが、他のもの(例:リスト、辞書、セット)は変更可能であり、それらを共有すると、 。この答えのコードでは、 'self.l'は同じ名前のクラス属性をシャドウしますが、' ListTestClass.l'を介してそのクラス属性に引き続きアクセスできることに注意してください。 –

+0

この記事は役に立ちましたか:はい、ベテランのNed Batchelderが書いた[Pythonの名前と値についての事実と神話](http://nedbatchelder.com/text/names.html) –

関連する問題