簡體   English   中英

如何以可靠的方式跟蹤python對象的實例?

[英]How to keep track of instances of python objects in a reliable way?

我希望能夠跟蹤幾何Point對象的實例,以便在自動命名新對象時知道已經“采用”了哪些名稱。

例如,如果已創建名為“A”,“B”和“C”的點,則下一個自動命名的Point將命名為“D”。 如果名為“D”的Point被刪除,或其引用丟失,則名稱“D”再次可用。

我的Point對象的主要屬性被定義為屬性,並且是非常標准的xyname

解決方案有問題和“沉重”的解決方法

我按照這里描述的那樣繼續使用weakref.WeakSet() 我把它添加到我的Point類:

# class attribute
instances = weakref.WeakSet()

@classmethod
def names_in_use(cls):
    return {p.name for p in Point.instances}

問題是,當我實例化一個Point然后刪除它時,大部分時間,但並非總是Point.instances刪除。 我注意到,如果我運行測試套件( pytest -x -vv -rw ),那么如果在測試中引發了某個異常 ,那么實例永遠不會被刪除(可能的解釋在下面稍后閱讀)。

在下面的測試代碼中,在第一次刪除p ,它總是從Point.instances刪除,但在第二次刪除p ,它永遠不會被刪除(測試結果總是相同的),最后一個assert語句失敗:

def test_instances():
    import sys
    p = Point(0, 0, 'A')
    del p
    sys.stderr.write('1 - Point.instances={}\n'.format(Point.instances))
    assert len(Point.instances) == 0
    assert Point.names_in_use() == set()
    p = Point(0, 0, 'A')
    with pytest.raises(TypeError) as excinfo:
        p.same_as('B')
    assert str(excinfo.value) == 'Can only test if another Point is at the ' \
        'same place. Got a <class \'str\'> instead.'
    del p
    sys.stderr.write('2 - Point.instances={}\n'.format(Point.instances))
    assert len(Point.instances) == 0

結果如下:

tests/04_geometry/01_point_test.py::test_instances FAILED

=============================================================================== FAILURES ===============================================================================
____________________________________________________________________________ test_instances ____________________________________________________________________________

    def test_instances():
        import sys
        p = Point(0, 0, 'A')
        del p
        sys.stderr.write('1 - Point.instances={}\n'.format(Point.instances))
        assert len(Point.instances) == 0
        assert Point.names_in_use() == set()
        p = Point(0, 0, 'A')
        with pytest.raises(TypeError) as excinfo:
            p.same_as('B')
        assert str(excinfo.value) == 'Can only test if another Point is at the ' \
            'same place. Got a <class \'str\'> instead.'
        del p
        sys.stderr.write('2 - Point.instances={}\n'.format(Point.instances))
>       assert len(Point.instances) == 0
E       assert 1 == 0
E        +  where 1 = len(<_weakrefset.WeakSet object at 0x7ffb986a5048>)
E        +    where <_weakrefset.WeakSet object at 0x7ffb986a5048> = Point.instances

tests/04_geometry/01_point_test.py:42: AssertionError
------------------------------------------------------------------------- Captured stderr call -------------------------------------------------------------------------
1 - Point.instances=<_weakrefset.WeakSet object at 0x7ffb986a5048>
2 - Point.instances=<_weakrefset.WeakSet object at 0x7ffb986a5048>
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
================================================================= 1 failed, 82 passed in 0.36 seconds ==================================================================

但是,在catched異常中測試的代碼不會創建新的Point實例:

def same_as(self, other):
    """Test geometric equality."""
    if not isinstance(other, Point):
        raise TypeError('Can only test if another Point is at the same '
                        'place. Got a {} instead.'.format(type(other)))
    return self.coordinates == other.coordinates

和坐標基本上是:

@property
def coordinates(self):
    return (self._x, self._y)

其中_x_y基本上包含數字。

原因似乎是(引自python的doc ):

CPython實現細節:參考周期可以防止對象的引用計數變為零。 在這種情況下,循環垃圾收集器稍后將檢測並刪除該循環。 引用周期的常見原因是在局部變量中捕獲到異常。

解決方法

將此方法添加到Point類:

def untrack(self):
    Point.instances.discard(self)

並且在del myPoint之前使用myPoint.untrack() (或者在以另一種方式失去對Point的引用之前)似乎解決了這個問題。

但是每次調用untrack()都非常重要......例如,在我的測試中,我需要“解決”許多點,以確保所有名稱都可用。

有沒有更好的方法來跟蹤這些實例? (通過改進這里使用的跟蹤方法,或通過任何其他更好的手段)。

不要嘗試根據整個程序中存在的所有Point對象來跟蹤可用名稱。 預測哪些對象將存在以及何時對象將不再存在是困難和不必要的,並且在不同的Python實現上它將表現得非常不同。

首先,為什么要嘗試強制點名稱唯一性? 例如,如果您在某個窗口中繪制了一個圖形並且您不希望在同一圖形中具有相同標簽的兩個點,則讓該圖形跟蹤其中的點並拒絕具有采用名稱的新點。 這也可以很容易地從圖中明確地刪除點,或者具有兩個具有獨立點名的圖。 還有許多其他上下文,其中類似的顯式容器對象可能是合理的。

如果這些是無法附加到某些幾何環境的自由浮點,那么為什么要將它們命名呢? 如果我想代表一點(3.5,2.4),我不在乎我是否將其命名為A或B或Bob,我當然不希望崩潰,因為程序中途的某些其他代碼決定調用他們的觀點也是鮑勃。 名稱或名稱沖突為何重要?

我不知道你的用例是什么,但對於大多數我可以想象,最好是在顯式容器中強制執行名稱唯一性,或者根本不強制執行名稱唯一性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM