如何找出Python对象丢失属性的原因?

时间:2011-03-17 23:37:24

标签: python pygtk

更新2013-02-08

我现在知道为什么我无法在一小段测试代码中重现这个问题。在一个小程序中,Python的垃圾收集器不是很活跃。我相信问题是Python正在收集一些仅在GObject中引用的对象。我认为这是一个涉及this bug的回归,或者是一个新的类似错误。

我认为这是因为我再次遇到了同样的问题,但是我自己的类(它只引用了GObject对象) - 这次整个 dict 在这个对象上消失了。如果我使用代码here来监控其中一个消失的属性,它就不会消失!似乎额外的引用保持了属性。闻起来像垃圾收集器问题。我通过在初始化期间将对象添加到全局列表来确认这一点...这也解决了现在发生的问题。

原始问题

我在使用PyGTK GUI时遇到了一些奇怪的行为。我有一个一直失去大量属性的对象。我试图确定这是否是我的代码,Python解释器或PyGTK中的错误。

我没有打电话给delattr()。我已经尝试通过使用始终引发异常的代码覆盖__delattr__()来检测是否正在调用我的对象的__delattr__()方法。我能够重现导致对象失去其属性的事件,但永远不会引发异常。我不确定另一种方法来找出哪些函数调用(如果有的话)导致对象丢失属性。

有问题的对象在任何时候都能正常工作,直到它突然失去我想要访问的属性。

在GUI中执行与失去属性的对象无关的某些操作后,属性丢失始终如一。我偶然发现了它;可能存在导致对象失去其属性的其他操作。

我已将print id(self)添加到访问消失属性的方法中。在属性消失之前和之后打印的id是相同的。

有关如何追查此问题根源的任何建议?

下面的参考代码: (注意:如果/当我想出一个可以重现问题的简化测试用例时,我会更新此代码。现在需要重现的代码。 bug太大了,不能在这里发帖。)

这是我的对象的类失去了它的属性。这显然是实际功能代码的最小化版本,但我使用它进行调试,问题仍然存在。

它是我的自定义MenuBar类的子类。 请注意,on_file_import__activate()方法通过其中一个父类连接到菜单项的信号。

class FluidClassManagerWindowMenu(MenuBar):
    menu_items = [("File",("Import",))]

    def __init__(self, parent):
        # XXX: different name than self.parent to see if it stops disappearing 
        self._xxx_my_parent = parent
        MenuBar.__init__(self, parent)

    def __delattr__(self,attr):
        # XXX: trying to find the cause for lost attributes
        traceback.print_stack()

    def on_file_import__activate(self, widget=None, data=None):
        # XXX: this is the same before and after the attributes disappear
        print id(self)
        # XXX: print the list of attributes to see what disappears
        print dir(self)
        # XXX: this works until the _xxx_my_parent attribute disappears
        print self._xxx_my_parent

如果你很好奇,这里是我的MenuBar类的完整源代码。它是pygtkhelpers SlaveView,继承自GObject。 pygtkhelpers委托执行与上述on_file_import__activate方法的自动信号连接。

class MenuBar(pygtkhelpers.delegates.SlaveView):        
    def __init__(self, parent):
        SlaveView.__init__(self)
        self.parent = parent

    def create_ui(self):
        menu_bar = gtk.MenuBar()
        menu_bar.set_pack_direction(gtk.PACK_DIRECTION_LTR)

        for menu_name, items in self.menu_items:
            menu = gtk.Menu()
            submenu = gtk.MenuItem(menu_name)
            submenu.set_submenu(menu)
            for item_name in items:
                if not item_name:
                    menu.append(gtk.MenuItem())
                    continue
                menuitem = gtk.MenuItem(item_name)
                fixed_item_name = item_name.lower().replace(' ','_')
                fixed_menu_name = menu_name.lower().replace(' ','_')
                attr_name = '%s_%s' % (fixed_menu_name,fixed_item_name)
                # set an attribute like self.edit_vial_layout
                # so pygtkhelpers can find the widget to connect the signal from
                setattr(self,attr_name,menuitem)
                menu.append(menuitem)
            menu_bar.append(submenu)
        self.vbox = gtk.VBox()
        self.vbox.pack_start(menu_bar)
        self.vbox.show_all()
        self.widget.add(self.vbox)

从对象中消失的属性列表

'_model', '_props', '_toplevel', '_xxx_my_parent', 'file_import', 'parent', 'slaves', 'testtesttest', 'vbox', 'widget'

属性parent正在消失;我尝试将其值分配给_xxx_my_parent ManagerWindowMenu.__init__()但它也消失了。我还在MenuBar.__init__中添加了一个我永远无法访问的新属性,名为testtesttest,它也消失了。

3 个答案:

答案 0 :(得分:2)

请记住,PyGTK中的对象经常从GObject继承。 GObject框架内可能会发生导致您丢失属性的活动。

答案 1 :(得分:2)

我有一个非常类似的问题。我有一个类(SearchWorker),它构建了一个小部件,用于在运行时添加到GUI。在该窗口小部件中,有一个按钮,其“点击”信号连接到其中一个SearchWorker功能。每当触发“clicked”信号时,SearchWorker自身对象的许多属性都消失了。

我在另一个类的另一个处理程序中创建了SearchWorker对象,如下所示:

worker = SearchWorker()

我假设一旦那个处理程序退出了一些奇怪的事情发生在worker引用后面的对象上。将SearchWorker的创建更改为:

self.worker = SearchWorker()

解决了我的问题。

答案 2 :(得分:0)

有点奇怪。顺便说一下,使用delattr__delattr__调用并不常见,所以我怀疑你是不是自己处理两个不同的对象,而是期待另一个对象。此外,它可能不是解释器的问题,如果出现问题,它会在更低的级别崩溃。