繁体   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