Erweiterung zu __subclasses__: __instances__

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Montag 24. November 2008, 01:14

Ich suchte eine Möglichkeit mir die Instanzen einer Klasse auflisten zu lassen. Da man __del__ ja nicht verwenden kann, erschien das kompliziert, da die Objekte allein durch ihre Präsenz in der Liste niemals gelöscht werden würden. __del__ kann / sollte man ja nicht verwenden. Lösung:

Code: Alles auswählen

import weakref

class Cls(object):
    _instances = []

    def __init__(self):
        self._instances.append(weakref.ref(self, self._del_weakref))

    @classmethod
    def __instances__(cls):
        return [inst() for inst in cls._instances if inst() is not None]

    @classmethod
    def _del_weakref(cls, weak_ref):
        del cls._instances[cls._instances.index(weak_ref)], weak_ref
Verwendung ist aquivalent zu __subclasses__. Ich kann mir vorstellen, das mancher das gebrauchen kann. Über das weakref Modul findet man hier sowieso noch wenig, schaden kanns also nicht :).

Anmerkung: Nicht in ipython ausprobieren, wahrscheinlich hält ipython selbst noch Referenzen auf das Objekt wenn es gelöscht wird. Zb über _. Ich hatte das Problem, bis ichs im normalen Python Interpreter getestet habe.

Ich glaube, ich werde das aber nicht "produktiv" verwenden und werde das eigentliche Problem anders angehen. Der Weg erscheint mir suboptimal (nicht die Implementation, generell sich Instanzen von einer Klasse zu holen, ist der Funktionsweise eines Singletons irgendwie ähnlich - und mir kommen mit einem Male Zweifel, ob das Konzept nicht unsicher ist, und zwar aus denselben Gründen wie bei __del__..)
BlackJack

Montag 24. November 2008, 08:24

@str1442: IPython merkt sich "alles":

Code: Alles auswählen

In [44]: 1
Out[44]: 1

In [45]: 2
Out[45]: 2

In [46]: 3
Out[46]: 3

In [47]: _44
Out[47]: 1

In [48]: _46
Out[48]: 3

In [49]: _45
Out[49]: 2
Kann manchmal ganz nützlich sein, insbesondere wenn man das Teil als "Taschenrechner" benutzt und auf alle Zwischenergebnisse noch mal zugreifen kann, auch wenn man sie nicht an Namen gebunden hat.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Montag 24. November 2008, 11:23

Die Faustregel ist, dass es ein Design-Fehler ist, wenn man alle Exemplare einer Klasse implizit kennen will. In der Regel kann man sie auch explizit merken und vergessen. Aber wenn schon, dann würde ich Weak References benutzen.

Code: Alles auswählen

class Foo(object):
  instancerefs = []
  def __new__(self):
    ...
    instancerefs.append(weakref.ref(self))

def get_instances(cls):
  return [ref() for ref in cls.instancerefs if ref() is not None]
Ich befürchte, obiger Code ist nicht threadsafe, denn zwischen den beiden ref()-Aufrufen das Objekt verschwinden kann und ich dann ein unerwartetes None zurückgebe. Muss wohl also dies benutzen:

Code: Alles auswählen

def get_instances(cls):
  return (o for o in (ref() for ref in cls.instancerefs) if o is not None)
Stefan
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Mittwoch 26. November 2008, 00:17

Die Faustregel ist, dass es ein Design-Fehler ist, wenn man alle Exemplare einer Klasse implizit kennen will.
Ja, das ist mir auch klar. Nur war es ein möglicher Lösungsweg, und das weakref Modul kannte ich noch nicht - im Forum findet man auch nicht viel dazu, und grade Anfänger möchten sowas vielleicht haben - auch wenn es unsauber ist. Man sollte immer alle Möglichkeiten aufzeigen, man kann immer wieder etwas lernen.
Antworten