如何以可靠的方式跟踪python对象的实例?

2024-04-23 19:26:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望能够跟踪几何点对象的实例,以便知道在自动命名新对象时已经“使用”了哪些名称。在

例如,如果已创建名为“A”、“B”和“C”的点,则下一个自动命名的点将命名为“D”。如果名为“D”的点被删除,或其引用丢失,则名称“D”将再次可用。在

我的Point对象的主要属性被定义为属性,它们是相当标准的xy和{}。在

解决问题和“繁重”的解决方案

我按照描述的here,使用了weakref.WeakSet()。我将此添加到我的Point类中:

# class attribute
instances = weakref.WeakSet()

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

问题是,当我实例化一个点然后删除它时,它大部分时间都是从Point.instances中删除的,但并非总是总是。我注意到,如果我运行测试套件(pytest -x -vv -r w),那么如果在测试中引发了某个异常,则实例从不被删除(可能的解释将在下面阅读)。在

在下面的测试代码中,在第一次删除p之后,它总是从Point.instances中删除,但是在第二次删除{}之后,它永远不会被删除(测试结果始终相同),最后一条assert语句失败:

^{pr2}$

结果是:

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 ==================================================================

但是,在捕获异常中测试的代码不会创建新的点实例:

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和{}基本上包含数字。在

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

CPython implementation detail: It is possible for a reference cycle to prevent the reference count of an object from going to zero. In this case, the cycle will be later detected and deleted by the cyclic garbage collector. A common cause of reference cycles is when an exception has been caught in a local variable.

解决方法

将此方法添加到Point类:

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

而在del myPoint之前使用myPoint.untrack()(或者在以另一种方式失去对点的引用之前)似乎可以解决这个问题。在

但是每次都要打电话给untrack(),这是相当沉重的。。。例如,在我的测试中,我需要“取消跟踪”许多点,以确保所有名称都可用。在

问题

有没有更好的方法来跟踪这些实例?(通过改进这里使用的跟踪方法,或者通过其他更好的方法)。在


Tags: theinstances实例方法intestselfobject
1条回答
网友
1楼 · 发布于 2024-04-23 19:26:31

不要试图基于整个程序中存在的所有Point对象跟踪可用名称。预测哪些对象将存在以及何时对象将停止存在是困难且不必要的,而且在不同的Python实现中,它的行为也会非常不同。在

首先,为什么要强制点名称的唯一性?例如,如果您在某个窗口中绘制地物,但不希望在同一地物中有两个具有相同标签的点,则让地物跟踪其中的点,并拒绝具有所取名称的新点。这也便于从地物中显式删除点,或使两个地物具有独立的点名称。在许多其他上下文中,类似的显式容器对象可能是合理的。在

如果这些是未附着到某些几何体环境的自由浮点,那么为什么要命名它们呢?如果我想在(3.5,2.4)处表示一个点,我不在乎我把它命名为a、B还是Bob,我当然不希望崩溃,因为程序中途的某个地方的其他代码也决定将它们的点命名为Bob。为什么名称或名称冲突很重要?在

我不知道您的用例是什么,但是我可以想象,最好是在显式容器中只强制名称唯一性,或者根本不强制名称唯一性。在

相关问题 更多 >