2012-11-10 22 views
9

Mam specyficzny problem python. Podczas wykonywania mojej aplikacji python gtk niektóre z moich obiektów klasy w tajemniczy sposób tracą atrybuty, powodując przerwanie niektórych funkcji mojego programu.Python classes tracące atrybuty

Trudno jest wyjaśnić, dlaczego tak się stało - nigdy celowo nie usuwam atrybutów, a klasy, o których mowa, dziedziczą po klasie, którą sam napisałem (i żadnych innych).

mogę wywołać problem wykonując pewne czynności wielokrotnie (na przykład generując wiele wywołań metody add_card - albo klikając szalenie lub otwierając plik, powodując add_card na miano około dwudziestu razy)

Naprawdę nie mogę się doczekać, i żałuję, że nie mam więcej informacji, które uznałem za przydatne.

Co może spowodować utratę atrybutów przez obiekt Pythona?

EDYCJA, Re. Pytania:

Oto przykładowe tracebacks związane z dwóch atrybutów I 'stracić':

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' 

I tu, gdzie są ustawione:

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 

Ta szczególna podklasa doesen't nazywają to nadklasa __init__, więc zestawy atrybutów są duplikowane w podklasie:

class DeckerRunnerList(DeckerGenericList): 

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

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

Wszystkie inne podklasy DeckerGenericList mają ten sam problem, a wszystkie one są zdefiniowane następująco:

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

Czy używasz wątków? Czy możesz pokazać kod? – Keith

+0

Jak i * kiedy * ustawiasz te atrybuty? Skąd wiesz, że zostały one usunięte? – Kos

+0

Po pierwsze, jeśli jeszcze tego nie zrobiłeś, sprawdź typ obiektu, aby upewnić się, że jest to instancja twojej klasy. Uważam, że śledzenie typów w słabo dynamicznie wpisywanych językach, takich jak Python, jest częstym źródłem błędów. . – ApproachingDarknessFish

Odpowiedz

1

Cóż można użyć właściwości i sprawdzić moduł do śledzenia zmian przypisywać tak:

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() 

Lub możesz użyć sys.settrace z tej odpowiedzi: https://stackoverflow.com/a/13404866/816449

+0

Po prostu użyłem tego kodu, aby dokładniej zbadać http://stackoverflow.com/questions/ 5346578/how-can-i-find-out-why-when-a-python-object-lose-atrybuty i było bardzo pomocne. Dzięki! –

3

PyQT (a więc być może także PyGtk lub inny framework) ma dziwne zachowanie, które faktycznie podnosząc AttributeError gdzieś w/poniżej kodu atrybutu będzie lly sprawi, że Python zgłosi, że atrybut nazwany na tym obiekcie nie istnieje.

Istnieje gdzieś (nieco bardziej oficjalne) opowiadanie o tym, dlaczego tak się dzieje (i właściwie jest to właściwe i zrozumiałe zachowanie). Ma to coś wspólnego z __getattr__ zdefiniowanym na podstawie klasy bazowej: meganizm, którym to działa, polega na tym, że jeśli wywołanie atrybutu na obiekcie powoduje AttributeError, wywoływana jest metoda __getattr__. Ale ta metoda nie ma pojęcia, czego "atrybutu" faktycznie nie znaleziono. A ponieważ (PyQt.QObject) __getattr__ został zaprojektowany w celu zaimplementowania "konkretnych" atrybutów, postanawia podnieść inny atrybut AttributeError, wymieniając atrybut "wywoływany".

W każdym razie, może być to, że dziedziczysz z obiektu, który używa gdzieś __getattr__ i że twój własny kod atrybutu wywołuje (inny) atrybut, który faktycznie nie istnieje.Co można zrobić, aby sprawdzić to:

@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!") 

Aktualizacja/Uwaga: Ponadto, jeśli wdrażają __getattr__ siebie na obiekcie DeckerGenericList, należy zachować ostrożność z nim do tego samego powodu! Podnoszenie atrybutów AttributeError w ciągu __getattr__ przez większość czasu prowadzi do pozornie dziwnych zachowań w ten sposób. Zauważ, że nie ma prostej łaty: "prawdziwy" AttributeError zawiera jedynie komunikat tekstowy (a nie konkretnie faktyczną nazwę atrybutu), ale o wiele ważniejszy: funkcja __getattr__ nigdy nie widzi oryginalnego AttributeError.

0

Myślę, że jest to bug z kolektorami śmieci. Miałem podobny problem i myślę, że zawęziłem go do GC.

Spróbuj dodać listę extra_references = [] do góry modułu zawierającego klasę, która traci atrybuty. W metodzie __init__ dla klasy, dodaj następujący kod:

global extra_references 
extra_references.append(self) 

, które zapewnią, że zawsze istnieje odniesienie do obiektu poza gobject.

Jeśli problem zniknie, to śmieciarka Pythona pochłonie twój obiekt, zanim skończysz. Pachnie bardzo podobnie do tego błędu (który rzekomo został naprawiony): https://bugzilla.gnome.org/show_bug.cgi?id=92955