【问题标题】:Python classes losing attributesPython 类丢失属性
【发布时间】:2012-11-10 11:02:49
【问题描述】:

我有一个特殊的 python 问题。在我的 gtk python 应用程序执行过程中,我的一些类对象神秘地丢失了属性,导致我的程序的一些功能中断。

很难说明为什么会发生这种情况 - 我从不故意删除属性,并且相关类继承自我自己(而不是其他人)编写的类。

我可以通过重复执行某个操作来触发问题(例如生成对add_card 方法的多次调用 - 通过疯狂点击或打开文件,导致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__,因此属性集在子类中重复:

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)

【问题讨论】:

  • 你在使用线程吗?你能显示一些代码吗?
  • DeckerRunnerList 定义在哪里?
  • FWIW, self.TABLE_NAME 应该是类属性,而不是实例属性
  • 发布一个实际使用类并引发该错误的示例代码。可能是显示行为的最小示例。没有这个,我们怎么知道你在做什么?
  • 看起来它可能有明显的 pygtk 风味......看看这个:stackoverflow.com/questions/5346578/…

标签: python class pygtk


【解决方案1】:

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__,出于同样的原因,请小心使用它!在 __getattr__ 中提高 AttributeError's 大多数时候会导致类似这样的看似奇怪的行为。请注意,没有简单的解决方法:“真实”的 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://stackoverflow.com/a/13404866/816449

    【讨论】:

    【解决方案3】:

    我认为这是一个垃圾收集器错误。我遇到了类似的问题,我想我已将其范围缩小到 GC。

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

    global extra_references
    extra_references.append(self)
    

    这将确保始终存在对 GObject 之外的对象的引用。

    如果问题消失了,那么 Python 的垃圾收集器会在您处理完对象之前将其吃掉。闻起来很像这个错误(据说已经修复):https://bugzilla.gnome.org/show_bug.cgi?id=92955

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-23
      • 2014-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-22
      • 1970-01-01
      相关资源
      最近更新 更多