Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
动态创建的类是否总是;遥不可及”;对于Python中的gc?_Python_Garbage Collection - Fatal编程技术网

动态创建的类是否总是;遥不可及”;对于Python中的gc?

动态创建的类是否总是;遥不可及”;对于Python中的gc?,python,garbage-collection,Python,Garbage Collection,我有一个关于Python中的垃圾收集的问题。在阅读了一些有见地的文章后,我决定搜索并删除代码中的所有循环引用,以便仅通过引用计数就可以销毁对象 为了查找现有的循环引用,我在unittest用例的tearDown方法中调用了gc.collect(),并在返回值>0时打印出警告。发现的大多数问题都可以通过重构或使用弱引用轻松解决 但过了一会儿,我遇到了一个相当奇怪的问题,最好用代码来表达: import gc gc.disable() def bar(): class Foo( objec

我有一个关于Python中的垃圾收集的问题。在阅读了一些有见地的文章后,我决定搜索并删除代码中的所有循环引用,以便仅通过引用计数就可以销毁对象

为了查找现有的循环引用,我在unittest用例的tearDown方法中调用了gc.collect(),并在返回值>0时打印出警告。发现的大多数问题都可以通过重构或使用弱引用轻松解决

但过了一会儿,我遇到了一个相当奇怪的问题,最好用代码来表达:

import gc
gc.disable()

def bar():
    class Foo( object ):
        pass

bar()
print( gc.collect() ) # prints 6
删除对bar()的调用时,gc.collect()会像预期的那样返回0

看起来,即使Foo是在函数栏的作用域内创建的,并且从未返回到外部,它仍然会粘住并导致垃圾收集器查找无法访问的对象

当把Foo移到酒吧范围之外时,一切又恢复正常了。但是,该解决方案不适用于我试图在受影响的代码中解决的问题(为序列化动态创建ctypes.Structures)

以下两种方法也不起作用:

import gc
gc.disable()

def bar():
    type( "Foo", ( object, ), {} )

bar()
print( gc.collect() ) # prints 6 again
甚至是非常“聪明”的人:

import gc
gc.disable()

import weakref

def bar():
    weakref.ref( type( "Foo", ( object, ), {} ) )

bar()
print( gc.collect() ) # still prints 6
最重要的是,这里有一个实际有效的例子。。。但仅在Python2中:

import gc
gc.disable()

def bar():
    class Foo(): # not subclassing object
        pass

bar()
print( gc.collect() ) # prints 0 - finally?
然而,上面的代码在Python3中再次打印出“6”——我怀疑,因为所有用户定义的类在Python3中都是新样式的类

那么,我是被Python2困住了,Python3中奇怪的“无法到达的对象”,还是我必须用手动垃圾收集来跟踪每次对bar的调用

*(关于使用gc.disable()运行Python的文章)


请参阅roippi的答案,了解上述行为是否符合预期

不过,作为将来的参考,这里有一个小的解决方法可以解决这个特定问题。不是说禁用gc对任何人来说都是正确的,但是如果你觉得它对你来说是正确的,我就是这样做的:

import gc
gc.disable()

def requiresGC( func ):
    def func_wrapper( *args, **kwargs ):
        result = func( *args, **kwargs )
        gc.collect()
        return result
    return func_wrapper

@requiresGC
def bar():
    class Foo( object ):
        pass

bar()
print( gc.collect() ) # prints 0
但是,请注意,如果bar()是一个定期调用的函数,则此修饰符将导致显著的速度减慢。然而,在我的例子(序列化)中,情况并非如此,将gc开销包含在一些特定函数中似乎是一种合理的折衷


感谢所有花时间快速回答问题的人!:-)

静态地或通过
type
声明一个新样式的类会创建一个循环引用(实际上不止一个)。下面是我能提供的最清楚的例子:

class Baz:
    pass

print(Baz in Baz.__mro__)
#True
Baz
\uu dict\uuu
中也有一些其他的循环引用,但您只需要一个


我真的无法为您提供任何解决方案——恐怕GC就是为了这个。如果您想进一步深入了解,我可以向您指出它已经存在了一段时间。

您还可以使用
weakref
模块进行引用,这不会妨碍垃圾收集。我建议不要关闭GC。它是Python中允许代码依赖的一部分。解释器核心创建引用循环,标准库创建引用循环,外部库创建引用循环,您可能在没有意识到的情况下创建引用循环,每个人都创建引用周期。听说在没有GC的情况下运行Python,我不禁想起Gentoo开发人员与想要设置并试图重新编译整个系统的用户之间的斗争。这种“优化”最终是要付出代价的,因为随着时间的推移,你几乎会手动重新发明gc,最终你会看到你的代码并想,“嗯,如果它能自动运行就好了…”