实例分配后,垃圾收集无法识别类的del

时间:2014-02-25 22:21:48

标签: python unit-testing garbage-collection subclassing

出于测试目的,我正在创建我想删除的临时类(在运行其他测试方法之前)。麻烦的是,即使在运行垃圾收集之后,[superclass].__subclasses__()仍会列出已删除的类。

以下是我的测试方法:

class Apple(Fruit):
    @staticmethod
    def mass(size):
        return size

class Orange(Fruit):
    @staticmethod
    def mass(size):
        return size

try:
    Apple()
    Orange()
    a1 = Apple(type='fuji')
finally:
    if 'a1' in locals():
        print 'del a1'
        del a1
    print gc.get_referrers(Apple)
    print gc.get_referrers(Orange)
    del Apple
    del Orange
    print Fruit.__subclasses__()
    gc.collect()
    print Fruit.__subclasses__()

输出如下:

del a1
[<frame object at 0xabcdef0>, (<class 'Apple'>, <class 'Fruit'>, <type 'object'>), <Apple object at 0x4443331>, {'a1': <Apple object at 0x4443331, 'self': <FruitTests testMethod=test_pass_Fruit_core>, 'Orange': <class 'Orange'>, 'Apple': <class 'Apple'>}]    
[<frame object at 0xabcdef0>, (<class 'Orange'>, <class 'Fruit'>, <type 'object'>), {'a1': <Apple object at 0x4443331, 'self': <FruitTests testMethod=test_pass_Fruit_core>, 'Orange': <class 'Orange'>, 'Apple': <class 'Apple'>}]
[<class 'Apple'>, <class 'Orange'>]
[<class 'Apple'>, <class 'Orange'>]

所涉及的所有类都没有明确定义的__del__(),尽管Fruit确实在__metaclass__ = abc.ABCMeta使用@abc.abstractmethodFruit.mass()装饰器。

剩余的类引用与将Fruit实例分配给变量有关:如果我删除包含a1的所有行,则最终Fruit.__subclasses__()返回{{1 - 即使裸构造函数[]仍在运行。

这对我来说是一个问题,因为另一个测试涉及水果互动(调用相关的待测方法Apple()),并且使用blends()调用来检查不同的组合Fruit.__subclasses__()的类型。我没有打算定义与这些测试类的交互,这让人感到困惑Fruit

有关这些参考文献为什么会留下来的任何提示都将不胜感激。

编辑:如果我在gc.collect()之后调用gc.get_referrers(Apple),我会在赋值之前得到一个“UnboundLocalError:局部变量'Apple'”Fruit使用“@classmethod”定义了许多方法@property“decorators,并引用另一个处理”blends()“的类......

垃圾回收后,blends()返回

gc.get_referrers(Fruit.__subclasses__()[0])

编辑:当我运行这一个测试方法时会出现问题。 (当我排队多次测试时也会出现这种情况。)我尝试重启我的IDE(PyCharm)并从命令行运行“./manage.py test FruitTests.test_pass_Fruit_core”。尽管特定的内存地址不同,但所有情况都会产生相同的结果。正在直接调用locals() - 我没有在任何地方使用别名。

编辑:定义Fruit的整个模块:

[{'a1': <Apple object at 0x4443331>, 'self': <FruitTests testMethod=test_pass_Fruit_core>, 'Orange': <class 'Orange'>, 'Apple': <class 'Apple'>}, <Apple object at 0x4443331>, (<class 'Apple'>, <class 'Fruit'>, <type 'object'>)]

在测试方法中,test_pass_Fruit_core(),“a1 = Apple()”和“a1 = Apple(type ='fuji')”产生相同的结果。将赋值放到“a1”没有任何区别,但如果我将调用放到“locals()”,垃圾收集按预期工作 - Apple在方法结束时不再作为Fruit的子类。

2 个答案:

答案 0 :(得分:0)

在垃圾收集环境中,对象的生命周期不是您的责任。因此,你不应该依赖于此。单元测试应测试您的业务逻辑,每个单元测试应测试一个单元的职责。对象的生命周期不是他们的责任,如果你的逻辑依赖于它,那么你要么使用了错误的环境,要么就是在滥用你当前的环境。

或许尝试使用Fruit或/和Pool等模式在您的实现中引入“有效”Factory的概念。如果您从“活动”对象列表中删除一个对象,您将不必担心GS的“不确定性”。

答案 1 :(得分:0)

在对locals()的调用中创建持久引用。要保证“del a1”在“try:”块内创建一个错误不会产生错误,在块之前指定“a1 = None”并跳过对locals()的调用。

最后,工作代码如下。与上面的第一个代码块比较:     Apple Apple(Fruit):         @staticmethod         def质量(大小):             返回大小

class Orange(Fruit):
    @staticmethod
    def mass(size):
        return size

a1 = None
try:
    Apple()
    Orange()
    a1 = Apple(type='fuji')
finally:
    del a1
    print gc.get_referrers(Apple)
    print gc.get_referrers(Orange)
    del Apple
    del Orange
    print Fruit.__subclasses__()
    gc.collect()
    sc = Fruit.__subclasses__()
    print sc
    if len(sc) > 0:
        print 42, gc.get_referrers(sc[0])