2012-11-10 171 views
9

我有一個奇怪的python問題。在執行我的gtk python應用程序的過程中,我的一些類對象神祕地失去了屬性,導致我的程序的一些功能中斷。Python類丟失屬性

很難說出爲什麼會出現這種情況 - 我從不故意刪除屬性,而且這些類繼承自我自己寫的類(也沒有其他類)。

我可以反覆做某些動作觸發的問題(例如產生許多調用的add_card方法 - 通過單擊瘋狂或打開一個文件,導致add_card被稱爲二十歲左右的時間)

我真的很茫然,我希望我有更多我認爲有用的信息給你。

什麼會導致python對象失去屬性?

EDIT,Re。問題:

下面是相關的兩個屬性我如回溯「丟失」:

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 

這種特殊的子類doesen't調用它的超類__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

+0

如何設置這些屬性?你怎麼知道他們被刪除? – Kos

+0

首先,如果你還沒有,檢查對象的類型,以確保它實際上是你的類的一個實例 - 我發現跟蹤Python等弱動態類型語言中的類型是一個常見的錯誤來源。 – ApproachingDarknessFish

回答

1

那麼你可以使用屬性和檢查模塊跟蹤屬性的改變是這樣的:

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

+0

我剛剛使用此代碼嘗試進一步調查http://stackoverflow.com/questions/ 5346578/how-can-i-find-out-why-when-a-python-object-lose-attributes,它非常有幫助。謝謝! –

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__自己,要小心它這個同樣的原因!在__getattr__中引發AttributeError將在大多數情況下導致這樣的奇怪行爲。請注意,沒有簡單的解決方法:'真正的'AttributeError只攜帶一個字符串消息(而不是具體的實際屬性名稱),但更重要的是:__getattr__函數從不會首先看到原始的AttributeError。

0

我認爲這是一個垃圾回收器的bug。我有類似的問題,我想我已經縮小到了GC。

嘗試將一個列表extra_references = []添加到包含丟失屬性的類的模塊的頂部。在該類的__init__方法中,添加以下代碼:

global extra_references 
extra_references.append(self) 

這將確保總是有GObject的的外部到對象的引用。

如果問題消失,那麼Python的垃圾收集器在完成之前正在吃掉你的對象。聞起來很像這個bug(它被認爲是固定的):https://bugzilla.gnome.org/show_bug.cgi?id=92955