【问题标题】:Garbage collection does not recognize del of a class after instance assignment实例分配后垃圾收集无法识别类的 del
【发布时间】:2014-02-25 22:21:47
【问题描述】:

出于测试目的,我正在创建要删除的临时类(在其他测试方法运行之前)。麻烦的是,[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 确实在Fruit.mass() 上使用了__metaclass__ = abc.ABCMeta@abc.abstractmethod 装饰器。

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

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

任何关于为什么这些参考文献仍然存在的提示将不胜感激。

编辑:如果我在 gc.collect() 之后调用 gc.get_referrers(Apple),我会得到一个“UnboundLocalError: local variable 'Apple' referenced before assignment”Fruit 使用“@classmethod”和“ @property" 装饰器,并引用另一个处理 "blends()" 的类...

垃圾回收后,gc.get_referrers(Fruit.__subclasses__()[0]) 返回

[{'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'>)]

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

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

from abc import abstractmethod, ABCMeta


class Fruit(object):
    __metaclass__ = ABCMeta

    def __init__(self, **kwargs):
        super(Fruit, self).__init__()

    @abstractmethod
    def mass(self, size):
        raise NotImplementedError

在测试方法中,test_pass_Fruit_core()、“a1 = Apple()”和“a1 = Apple(type='fuji')”产生相同的结果。放弃对“a1”的分配没有什么区别,但如果我放弃对“locals()”的调用,垃圾收集将按预期工作——在方法结束时,Apple 不再作为 Fruit 的子类使用。

【问题讨论】:

  • 垃圾回收后gc.get_referrers(Apple)会打印什么?
  • 对子类的引用是弱引用,所以 something else 仍然是指这些类。 ABC 架构也包含引用,再次使用弱引用。
  • 使用gc.gen_referrers(Fruit.__subclasses__()[0])
  • 那么,是否还有另一个FruitTests.test_pass_Fruit_core 测试方法在某个地方保留着另一个这些子类的集合?或者您是否将locals() 的输出存储在某处,看起来像locals() 字典。

标签: python unit-testing garbage-collection subclassing


【解决方案1】:

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

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

【讨论】:

  • 引入一个只在测试中使用的属性似乎很尴尬。上述测试的目的是验证 @abc.abstractmethod 装饰器是否存在并按照宣传的方式工作。 (也就是说,当且仅当子类提供质量的显式定义时,这些类才应该是可实例化的。)
  • @SarahMesser 这看起来又是在尝试测试不属于您的责任。 abc 模块应该有自己的单元测试来证明其工作正常。
【解决方案2】:

在对 locals() 的调用中创建了持久引用。为保证“del a1”在“try:”块内创建时不会产生错误,请在该块之前分配“a1 = None”并跳过对 locals() 的调用。

最后,工作代码如下。与上面的第一个代码块比较: 苹果类(水果): @静态方法 默认质量(尺寸): 返回大小

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])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 2013-04-20
    • 2013-03-09
    • 1970-01-01
    • 2020-11-29
    • 2011-05-28
    相关资源
    最近更新 更多