Instanzen einer Klasse erfassen / auflisten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Um die Instanzen einer Klasse zu erfassen, habe ich das nachfolgende Script erstellt.
Leider finde ich nicht den Fehler, der zu folgender Meldung führt:
AttributeError: 'Kind' object has no attribute '_instances'
Könntet Ihr mir bitte helfen?

import datetime
import weakref

_instances = set()

class Kind:

def __init__(self, varname, vorname, nachname, geburtstag):
self.varname = varname
self.vorname = vorname
self.nachname = nachname
self.geburtstag = geburtstag
self._instances.add(weakref.ref(self))

def alter(self):
today = datetime.date.today()
alter = today.year - self.geburtstag.year
if today < datetime.date(today.year, self.geburtstag.month, self.geburtstag.day):
alter -= 1
return alter

@classmethod
def getinstances(cls):
x = set()
for ref in cls._instances:
obj = ref()
if obj is not None:
yield obj
else:
x.add(ref)
cls._instances -= x

janpeter = Kind(
'janpeter',
'Jan-Peter',
'Schneider',
datetime.date(1981, 10, 28)
)
katrin = Kind(
'katrin',
'Katrin',
'Schneider',
datetime.date(1983, 9, 16)
)
robert = Kind(
'robert',
'Robert',
'Schneider',
datetime.date(1986, 4, 28)
)
stefan = Kind(
'stefan',
'Stefan',
'Schneider',
datetime.date(1995, 3, 7)
)
for obj in Kind.getinstances():
print(obj.varname)
Sirius3
User
Beiträge: 17755
Registriert: Sonntag 21. Oktober 2012, 17:20

Was ist ein Varname? Warum willst Du überhaupt alle Instanzen wissen? Entweder Du hast eine Liste mit Instanzen, die Du für einen bestimmten Zweck brauchst, oder nicht. Bei Dir also alle Kinder in eine Liste packen und gut ist.
_instances sollte ein Klassenattribut sein und vom Typ WeakSet. getinstances ist dann überflüssig.
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Vielen Dank für die prompte Antwort. Ich hätte gern alle Instanzen im nachhinein abgegriffen, auch wenn sie mal ergänzt werden, um dann über das vollständige Set iterieren zu können, auch wenn es mal mehr werden (was nicht für Kinder gilt, das ist nur ein Übungsbeispiel)
Sirius3
User
Beiträge: 17755
Registriert: Sonntag 21. Oktober 2012, 17:20

Außer für Spielereien ist das aber nie sinnvoll oder gut. Was soll denn dreh Anwendungsfall sein?
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Eine vollständige Auflistung aller Instanzen soll letztendlich genau das absichern, was Du als Voraussetzung beschrieben hast: Die prinzipiell vorher - aber nicht immer - vorhandene (vollständige) Liste. Es geht mir um die Frage: Habe ich aller meiner Kontakte als Instanzen erfasst? Und wenn nicht: wer fehlt?
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Dann solltest du sie auf jeden Fall in eine Liste tun. Aber die Liste hat nichts in der Klasse zu suchen.
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Ich will die Liste nicht in die Klasse bringen, ich will sie aus der Klasse generieren.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Du versuchst die ganz offensichtlich mit _instances in die Klasse zu bringen. Und die gehört da nicht hin.
Eine Klasse bildet ein Objekt ab. Die Liste musst du da führen, wo die Instanzen erstellt werden, bzw. dort wo sie "aufgehoben" werden müssen. Das ist nicht in der Klasse. Das Design ist kaputt.
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Kaputt hin oder her: Meine Kontakte habe ich in verschiedenen Listen - teilweise auf Papier. Und zukünftig habe ich sie als Instanzen. Und jetzt möchte ich sicher sein, dass ich alle Kontakte instantiiert habe. Und dafür brauche ich eine Zusammenstellung aller Instanzen. Damit ich anschließend in meinen Ursprungs-Kontaktlisten aushaken kann.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Das geht, ist aber selten eine gute Idee:

Code: Alles auswählen

class Test:
    instances = list()
    def __init__(self):
        self.__class__.instances.append(self)
    def __repr__(self):
        return 'id: {}'.format(id(self))

tests = [Test() for _ in range(5)]
del tests

for item in Test.instances:
    print(item)    
Wie Du siehst, kann nun der Garbage Collector nicht mehr greifen und Du erzeugst potentielle Speicherlecks. Besser ist es, Instanzen außerhalb der eigenen Klasse zu verwalten. Das macht auch den Code besser verständlich:

Code: Alles auswählen

class Test:
    def __repr__(self):
        return 'id: {}'.format(id(self))

tests = [Test() for _ in range(5)]

for item in tests:
    print(item)    
In tests sind auch hier alle Deine Instanzen, die Du dann auf Papier abhaken kannst.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Wo ist denn jetzt dein Problem die Instanzen in eine Liste zu packen?
Ich verstehe deine Sorgen nicht.
Ob du die nun auf Papier oder auf Steintafeln hast ist doch völlig egal für die Lösung.

Spätestens wenn du die speichern willst, musst du sie ja auch wieder laden. Und dort wo du sie lädst, packst du sie halt in eine Liste.

Code: Alles auswählen

import datetime

class Kind:

    def __init__(self, varname, vorname, nachname, geburtstag):
        self.varname = varname
        self.vorname = vorname
        self.nachname = nachname
        self.geburtstag = geburtstag

    def alter(self):
        today = datetime.date.today()
        alter = today.year - self.geburtstag.year
        if today < datetime.date(today.year, self.geburtstag.month, self.geburtstag.day):
            alter -= 1
        return alter


def main():
    kinder = []
    kinder.append(Kind('janpeter','Jan-Peter','Schneider',
                        datetime.date(1981, 10, 28)))
    kinder.append(Kind('katrin','Katrin','Schneider',
                        datetime.date(1983, 9, 16)))
    kinder.append(Kind('stefan','Stefan','Schneider',
                        datetime.date(1995, 3, 7)))
    kinder.append(Kind('stefan','Stefan','Schneider',
                        datetime.date(1995, 3, 7)))
    for kind in kinder:
        print(kind.vorname)
    

if __name__ == "__main__":
    main()
Sirius3
User
Beiträge: 17755
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich verstehe nicht, wo die Kontakte denn an unterschiedlichen Stellen erzeugt werden sollen? Im Programmcode gibt es meist nur eine Stelle, an der etwas generiert wird, egal aus was. DAAD mit den Abgleich verstehe ich auch nicht. Mit was willst du denn vergleichen.
Ich habe die Befürchtung, Du verwechselst ein Programm mit einem Notizzettel, wo ungeordnet was notiert wird. Kannst Du genauer beschrieben, was Du vorhast?

@kbr: deshalb verwendet der OP ja weakref.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Sirius3 hat geschrieben: Sonntag 8. März 2020, 17:17 @kbr: deshalb verwendet der OP ja weakref.
Ja, stimmt. Was zwar Speicherlecks entgegenwirkt, aber die weakref-Instanzen aller gelöschten Objekte weiter mitschleppt. So etwas ist aus meiner Sicht zumeist ein Designfehler.
hcshm
User
Beiträge: 48
Registriert: Dienstag 11. Februar 2020, 08:23

Vielen Dank, vor allem auch für die Erläuterungen warum/weshalb!
Antworten