2014-02-25 22 views
1

后,出于测试目的,我创造,我想删除(其它测试方法运行之前)临时类。麻烦的是,[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.abstractmethod装修工Fruit.mass()

残存类引用有事情做与Fruit实例变量的赋值:如果我删除包含a1,最终Fruit.__subclasses__()返回[]的所有行 - 即使裸构造Apple()仍然运行。

这对我来说是一个问题,因为另一个测试是关于水果的相互作用(调用相关的方法将要测试blends()),并使用一个Fruit.__subclasses__()调用查看不同类型的Fruit的组合。我没有打算定义与这些测试类别的交互,这令人困惑blends()

为什么这些引用坚持围绕任何提示,将不胜感激。编辑:如果我在gc.collect()之后调用gc.get_referrers(Apple),我得到一个“UnboundLocalError:本地变量Apple',它在赋值之前被引用”Fruit用“@classmethod”定义了很多方法,并且“@property”装饰,并引用另一类处理“混合物()” ......

垃圾收集后,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”。所有情况下都会得到相同的结果,但具体的内存地址有所不同。当地人()被直接调用 - 我没有别名的别名。

编辑:整个模块限定水果:

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 =苹果()” 和 “A1 =苹果(类型= '富士')” 产生的相同的结果。将任务分配给“a1”没有任何区别,但如果我将调用放到“locals()”,垃圾回收按预期工作 - Apple在方法结束时不再可用作Fruit的子类。

+0

又是什么'gc.get_referrers(苹果)'垃圾回收后打印? –

+0

对子类的引用是弱引用,所以*其他*仍然指向这些类。 ABC体系结构也包含引用,同样使用弱引用。 –

+0

使用'gc.gen_referrers(Fruit .__子类__()[0])''。 –

回答

0

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

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

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

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

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

+0

引入仅用于测试的属性似乎很尴尬。上述测试的目的是验证@ abc.abstractmethod装饰器是否存在并按广告的方式工作。 (也就是说,当且仅当子类提供了一个明确的质量定义时,这些类应该是可实例化的。) –

+0

@SarahMesser这又看起来像试图测试一些不是你的责任的东西。 'abc'模块应该有它自己的单元测试,证明它的正常工作。 – BartoszKP