Python类失去属性

10

我有一个奇怪的Python问题。在执行我的GTK Python应用程序的过程中,我的某些类对象会神秘地丢失属性,导致程序的一些功能出现问题。

很难说出发生这种情况的原因 - 我从未有意删除属性,而且涉及到的类都是继承自我自己编写的类(和其他类没有关系)。

我可以通过重复执行某个操作来触发该问题(例如通过疯狂点击或打开文件生成多次调用add_card方法 - 使其被调用二十次左右)。

我真的很迷茫,希望我有更多有用的信息可以提供给您。

什么可能导致Python对象丢失属性?

编辑,关于问题:

以下是与我“丢失”的两个属性相关的示例跟踪:

Traceback (most recent call last):
  File "lib/genericlist.py", line 90, in cursor_changed
    if self.viewer:
AttributeError: 'DeckerRunnerList' object has no attribute 'viewer'



Traceback (most recent call last):
  File "lib/genericlist.py", line 100, in row_activated
    selection = self.TABLE_NAME+"&&"+text
AttributeError: 'DeckerRunnerList' object has no attribute 'TABLE_NAME'

这里是它们被设置的地方:

class DeckerGenericList(object):   

    def __init__(self, database, viewer=None, deck=None):

        super(DeckerGenericList, self).__init__()

        self.database = database
        self.viewer = viewer
        self.deck = deck
        #TABLE_NAME is set in the subclass constructor

这个特定的子类没有调用它的父类的__init__,因此属性集在子类中是重复的:

这个特定的子类没有调用其父类的__init__,因此属性设置在子类中被复制:

class DeckerRunnerList(DeckerGenericList):

      def __init__(self, database, viewer=None, deck=None):

        self.database = database
        self.viewer = viewer
        self.deck = deck
        self.TABLE_NAME = "runners"

所有其他的DeckerGenericList子类都有同样的问题,并且它们都定义为这样:

class DeckerGearList(DeckerGenericList):

    def __init__(self, database, viewer=None, deck=None):

        self.TABLE_NAME = "gear"
        #... some other class attributes

        super(DeckerGearList, self).__init__(database, viewer=viewer, deck=deck)

3
您正在使用线程吗?您能展示一些代码吗? - Keith
2
DeckerRunnerList 定义在哪里? - Eric
3
顺便说一句,self.TABLE_NAME 应该是一个类属性,而不是实例属性。 - Eric
3
请张贴一个实际使用这些类并引发错误的示例代码。最好提供一个最小化的示例以展示该行为。如果没有此内容,我们将无法知道您在做什么。 - Bakuriu
3
看起来它可能是一个明显的pygtk风格...看看这个:https://dev59.com/ZFXTa4cB1Zd3GeqP0lU3 - Sheena
显示剩余15条评论
3个回答

3

PyQT(因此也可能包括PyGtk或其他框架)有一个奇怪的行为,即在属性代码内/下方实际引发AttributeError将使Python报告所调用对象上的属性不存在。

某个(更正式的)故事确实解释了为什么会出现这种情况(实际上是正确和可理解的行为)。它与在基类上定义__getattr__有关:该机制的工作方式是,如果在对象上调用属性导致AttributeError,则调用__getattr__方法。但是该方法不知道实际未找到的“属性”是什么。由于(PyQt.QObject)__getattr__旨在实现“特定”属性,因此它决定引发另一个AttributeError,提到了“被调用”的属性。

无论如何,可能的原因是您继承自使用__getattr__的对象,并且您自己的属性代码确实调用(另一个)确实不存在的属性。您可以执行以下操作来检查:

@property
def myproperty:
   try:
      doStuff..
   except AttributeError as e:
      print "Got ya! It is withing the attribute:", repr(e)
      raise ValueError("Got an AttributeError within my attribute!")

更新/注:此外,如果您在DeckerGenericList对象上自己实现__getattr__,请小心处理它,因为出现AttributeError的情况大多会导致看似奇怪的行为。请注意,没有简单的解决方法:'真正的' AttributeError只携带一个字符串消息(而不是实际属性名称),但更重要的是,__getattr__函数从一开始就无法看到原始AttributeError。


2
你可以使用property和inspect模块来跟踪属性的更改,就像这样:
import sys
import inspect

def from_where_called():
    info = inspect.getframeinfo(sys._getframe(2))
    code = info.code_context[0] if info.code_context else ''
    return '%s:%s %s' % (info.filename, info.lineno, code)

def add_watched_attribute(name):  
    def attr_watch_get(self):
        value = getattr(self, '_' + name, 'unset')
        print from_where_called(), name, 'is', value
        return value

    def attr_watch_set(self, value):
        print from_where_called(), name, 'set to', value
        setattr(self, '_' + name, value)

    def attr_watch_delete(self):
        print from_where_called(), name, 'deleted'
        delattr(self,'_' + name)

    sys._getframe(1).f_locals[name] = property(
        attr_watch_get, attr_watch_set, attr_watch_delete
    )


class InspectedClass(object):
    add_watched_attribute('victim')

    def __init__(self):
        self.victim = 2

    def kill(self):
        del self.victim


x = InspectedClass()
x.victim = 'asdf'
x.kill()

或者您可以使用此答案中的sys.settrace:https://dev59.com/s2Yr5IYBdhLWcg3wxM-8#13404866


我刚刚使用了这段代码,试图进一步调查https://dev59.com/ZFXTa4cB1Zd3GeqP0lU3,它非常有帮助。谢谢! - Steven T. Snyder

1

我认为这是垃圾回收器的一个bug。我曾经遇到过类似的问题,我认为我已经将其缩小到了GC。

尝试在包含丢失属性的类的模块顶部添加一个列表extra_references = []。在类的__init__方法中,添加以下代码:

global extra_references
extra_references.append(self)

这将确保在 GObject 外部始终有一个对该对象的引用。

如果问题消失,则表示 Python 的垃圾收集器在你使用完该对象之前清除了它。这很像这个 bug(据说已经修复): https://bugzilla.gnome.org/show_bug.cgi?id=92955


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接