instanzen einer klasse und all ihrer unterklassen zählen

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
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

in folgendem beispiel will ich in klasse A alle erstellten instanzen von A, B, C und D zählen.

Code: Alles auswählen


class A(object):
    counter = 0
    
    def __init__( self ):
        self.__class__.counter += 1

class B(A): pass

class C(A): pass

class D(B): pass


>>> A();B();C();D()
<__main__.A object at 0x00B53830>
<__main__.B object at 0x00B58BD0>
<__main__.C object at 0x00B53830>
<__main__.D object at 0x00B58BD0>
>>> A.counter
1
>>> B.counter
2
>>> C.counter
2
>>> D.counter
3
mir ist klar warum das so aussieht, aber ich hätte gerne folgendes:

Code: Alles auswählen

>>> A.counter
4
>>> B.counter
2
>>> C.counter
1
>>> D.counter
1
wie macht man das?
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Moin!
Jona hat geschrieben:mir ist klar warum das so aussieht, aber ich hätte gerne folgendes:

Code: Alles auswählen

>>> A.counter
4
>>> B.counter
2
>>> C.counter
1
>>> D.counter
1
wie macht man das?
Mit Minus anstatt Plus :wink: .

Nein, im ernst: Was willst du denn genau erreichen?


Gruß,
Manuel
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

ich möchte dass der counter in superklasse A alle instanzen zählt.
und zwar alle instanzen von A sowie alle instanzen von von A abgeleiteten klassen.

nach

Code: Alles auswählen

A();B();C();D()
wären das also 4.
und idealerweise sollte der counter von C und D auf 1 stehen (da dort jeweils nur eine instanz erzeugt wurde) und der counter von B auf 2 ( einmal B und einmal D was von B abgeleitet ist)

klar?
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Hi!

Der folgende Code sollte dein Problem lösen.

Code: Alles auswählen


class A(object):
    def __new__(cls):
        cls.counter = 0 # jede Klasse braucht ihre eigene counter-Variable
        return object.__new__(cls)

    def __init__(self):
        self.__class__.counter += 1
        self._count_bases(self.__class__.__bases__)

    @classmethod
    def _count_bases(cls, bases):
        """Counter fuer jede Superklasse erhoehen."""
        for cls in bases:
            try:
                cls.counter += 1
            except AttributeError:
                pass
            cls._count_bases(cls.__bases__)

class B(A): pass

class C(A): pass

class D(B): pass 

Gruß,
Manuel
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

hey danke dir :)
helduel hat geschrieben:

Code: Alles auswählen


class A(object):
    def __new__(cls):
        cls.counter = 0 # jede Klasse braucht ihre eigene counter-Variable
        return object.__new__(cls)

    def __init__(self):
        self.__class__.counter += 1
        self._count_bases(self.__class__.__bases__)

    @classmethod
    def _count_bases(cls, bases):
        """Counter fuer jede Superklasse erhoehen."""
        for cls in bases:
            try:
                cls.counter += 1
            except AttributeError:
                pass
            cls._count_bases(cls.__bases__)

class B(A): pass

class C(A): pass

class D(B): pass 

das tut es schon fast, aber:

zeile 5 setzt den counter immer wieder auf 0.
daher kann jede klasse nur einmal instanziert werden.
habe versucht mit hasattr(cls, "counter") zu testen ob counter schon da ist. das trifft aber für B, C, D zu, falls A vorher instanziert wurde.
(kann man irgendwie feststellen, ob die variable der klasse selbst gehört und nicht geerbt wurde?)

zeile 20 crashed wenn die rekursion "object" erreicht.

habe das jetzt so:

Code: Alles auswählen

class A(object):
    
    counter = 0
    
    def __init__(self):
        self.__class__.counter += 1
        self._count_bases(self.__class__.__bases__)

    @classmethod
    def _count_bases(cls, bases):
        """Counter fuer jede Superklasse erhoehen."""
        for cls in bases:
            try:
                cls.counter += 1
            except AttributeError:
                pass
            if hasattr(cls, "_count_bases"):
                cls._count_bases(cls.__bases__)

class B(A): counter = 0

class C(A): counter = 0

class D(B): counter = 0
funktioniert, aber schön ist das irgendwie nicht :/

EDIT:

das hasattr ist überflüssig, der rekursive aufruf kommt einfach in das try

Code: Alles auswählen

    @classmethod
    def _count_bases(cls, bases):
        """Counter fuer jede Superklasse erhoehen."""
        for cls in bases:
            try:
                cls.counter += 1
                cls._count_bases(cls.__bases__)                
            except AttributeError:
                pass
BlackJack

Wozu brauchst Du das überhaupt?
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

in meinem programm werden objekte dynamisch nach nutzereingaben erstellt.
beispiel:

backwaren<|--kuchen<|--torte


das können die nutzer "backen".

ich möchte wissen wieviele backstücke es insgesamt gibt, wieviele kuchen wieviele torten...
BlackJack

Ich weiss nicht ob es so sinnvoll ist das bei den *Klassen* zu speichern. Man kann dann nicht mal eben so ein Backstück zu Testwecken erstellen ohne das hochgezählt wird. Und so kann man auch nur Backstücke zählen, die irgendwann einmal existierten. Falls Du `__del__()` zum runterzählen benutzen wolltest vergiss das ganz schnell wieder. Die Methode ist genauso unbrauchbar wie Java's `finalize()`.
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

wahrscheinlich hast du recht.

vor allem wenn man dafür so einen aufwand veranstalten muss.
rekursiv über __bases__, kapiert doch keiner was da abgeht.
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

Jona hat geschrieben:wahrscheinlich hast du recht.

vor allem wenn man dafür so einen aufwand veranstalten muss.
rekursiv über __bases__, kapiert doch keiner was da abgeht.
In Anspielung auf "Name Mangling" im Tutorial/super aus dem Wiki
[wiki]Tutorial/super[/wiki]
lässt sich dies nebenher erledigen ;-)
Da D von A "erbt", tritt A 2x auf, sonst 1x.

Code: Alles auswählen

class mro_object(object):
    def __init__(self):
        cls = self.__class__
        try:
            if not self._iter_mro: pass
        except:
            mro = cls.mro()[cls.mro().index(cls):]
            self._iter_mro = (cls for cls in mro)

class R(mro_object):
    def __init__(self):
        mro_object.__init__(self)
        self.__mro = self._iter_mro.next()
        super(self.__mro,self).__init__()
        try:
            self.__mro.__counter += 1
        except: self.__mro.__counter = 1
        
    def save(self):        
        print "r"

class A(R):
    def __init__(self):
        mro_object.__init__(self)
        self.__mro = self._iter_mro.next()
        super(self.__mro,self).__init__()
        try:
            self.__mro.__counter += 1
        except: self.__mro.__counter = 1
        
    def save(self):
        super(self.__mro,self).save()
        print "a"

class B(R):
    def __init__(self):
        mro_object.__init__(self)
        self.__mro = self._iter_mro.next()
        super(self.__mro,self).__init__()
        try:
            self.__mro.__counter += 1
        except: self.__mro.__counter = 1
        
    def save(self):
        super(self.__mro,self).save()
        print "b"

class D(B,A):
    def __init__(self):
        mro_object.__init__(self)
        self.__mro = self._iter_mro.next()
        super(self.__mro,self).__init__()
        try:
            self.__mro.__counter += 1
        except: self.__mro.__counter = 1
        
    def save(self):
        super(self.__mro,self).save()
        print "d"


>>> d=D();b=B();a=A();r=R()
>>> d.save()
r
a
b
d
>>> b.save()
r
b
>>> a.save()
r
a
>>> r.save()
r
>>> D._D__counter
1
>>> B._B__counter
2
>>> A._A__counter
2
>>> R._R__counter
4
>>> 
Antworten