Kombination von newstyle-classes und oldstyle-classes

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.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Kann es sein, dass zumindest einige der Features der Klassen neuen Stils verloren gehen, wenn ich diese Klasse von einer Klasse alten Stils ableite?

Konkret habe ich eine Klasse, die properties nutzt - funktionierte auch prima.
Jetzt habe ich diese Klasse von einer anderen Klasse alten Stils - auf die ich keinen Einfluss habe - abgeleitet und es funktioniert überhaupt nicht mehr.
"Überhaupt nicht" heißt, es gibt keine Fehlermeldungen, sondern es passiert nicht mehr das, was passieren sollte. Statt z.B. die über property festgelegten getter und setter zu verwenden, werden noch nicht vorhandene Attribute einfach erzeugt bzw. überschrieben oder ähnliches. Jedenfalls mit unerfreulichen Ergebnissen. Und die __slots__() werden einfach ignoriert.

Frage: Gibt es eine Möglichkeit, wie man auch bei Ableitung von einer Klasse alten Stils von diesen Features der newstyle-Klassen Gebrauch machen kann?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Von der "alten" Klasse und `object` erben.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Trundle hat geschrieben:Von der "alten" Klasse und `object` erben.
Das hilft nicht. Hatte ich schon probiert.

Habe eine newstyle-Klasse mit funktionierenden properties und slots außer von object noch von einer oldstyle-Klasse abgeleitet, die außer einem "pass" nichts gemacht hat.
Ergebnis: Funktioniert nicht mehr!

EDIT:
Ergänzung/Korrektur/Präzisierung: propoerties scheinen in diesem Fall zu funktionieren, __slots__ aber nicht. Ich experimentiere weiter ...
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Kann ich nicht nachvollziehen:

Code: Alles auswählen

class OldMan:
    pass

class My(OldMan, object):
    def __init__(self):
        self.working = 'nothing'
    def _work(self):
        return 'Working at %s' % self.working
    work = property(_work)

my = My()
print my.work
my.work = 'old-style-classes'
print my.work
Könntest du ein Beispiel geben, dass wir uns den Code nicht selbst ausdenken müssen?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Und was heisst `__slots__` funktionieren nicht? Das ist nicht vererbbar, eine Unterklasse hat wieder ein `__dict__`.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Leonidas hat geschrieben:Könntest du ein Beispiel geben, dass wir uns den Code nicht selbst ausdenken müssen?

Code: Alles auswählen

class Oldstyle:
    pass

class Newstyle(Oldstyle,object):

    def __init__(self):
        self._x = 0

    def _getx(self):
        return self._x

    def _setx(self,x):
        self._x = 2*x

    __slots__ = ["x","_x"]
    x = property(_getx,_setx)

obj = Newstyle()
obj.x = 1
obj.y = 5
print obj.x,obj.y
Leite ich Newstyle nur von object ab, liefert Zeile 20 - wie gewünscht - einen AttributError.
Leite ich zusätzlich von Oldstyle ab, funktioniert zwar der Weg über property() noch, aber der AttributError bleibt aus.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

pütone hat geschrieben:Leite ich Newstyle nur von object ab, liefert Zeile 20 - wie gewünscht - einen AttributError.
Leite ich zusätzlich von Oldstyle ab, funktioniert zwar der Weg über property() noch, aber der AttributError bleibt aus.
Das wäre mit nur zwei New-Style-Klassen aber genauso, wenn nur eine davon `__slots__` definiert hat. Die andere hat dann eben ein `__dict__`-Attribut, und deshalb gibt es bei Zuweisungen keinen AttributeError.

Edit: typo
Zuletzt geändert von Trundle am Samstag 22. März 2008, 22:15, insgesamt 1-mal geändert.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Mit anderen Worten:
__slots__ kann ich nur dann gebrauchen, wenn alle vererbenden Klassen auch über ein __slots__-Attribut verfügen.
Habe ich auf eine dieser Klassen keinen Einfluss, kann ich slots vergessen.

Richtig?
BlackJack

Verkürze es auf `__slots__` kannst Du vergessen. Denn wenn mehr als eine Basisklasse welche hat bekommst Du das hier:

Code: Alles auswählen

<type 'exceptions.TypeError'>: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Das wird ja immer schlimmer ....
Hab's gerade ausprobiert. Frust!

Bis heute Nachmittag war ich von der Kombination property/slots noch ganz angetan. Jetzt macht sich allmählich Ernüchterung breit.

Soweit ich das bisher verstanden habe, könnte ich das gleiche im Prinzip auch mit __getattr__ und __setattr__ realisieren. Richtig?

Falls richtig: Könnte jemand mein Codebeispiel (oder was vergleichbar einfaches) mal mit __getattr__ und __setattr__ umsetzen?
Ich habe schon damit experimentiert, aber meist ungewollt Schleifen erzeugt und bin nicht glücklich damit geworden.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Was willst du denn genau erreichen?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Zum einen will ich erreichen, dass ich unter Verzicht auf explizite getter/setter-Methoden (scheinbar) direkt über Datenattribute operieren kann, wobei dieser scheinbar direkte Zugriff eben doch keiner ist. Das gelingt mit properties und da genügt ja in der Tat die zusätzliche Ableitung von einer newstyle-class, um es nutzen zu können.

Außerdem hätte ich gerne, dass ich in der Klassendefinition die Menge der zulässigen Attribute festlegen kann, so dass "von außen" keine zusätzlichen Attribute an ein Exemplar der Klasse gebunden werden können. Also genau das, was ich mit slots erreichen kann - zumindest solange, wie ich meine Klassen nur von "object" ableite, wie ich inzwischen gelernt habe.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

pütone hat geschrieben:Außerdem hätte ich gerne, dass ich in der Klassendefinition die Menge der zulässigen Attribute festlegen kann, so dass "von außen" keine zusätzlichen Attribute an ein Exemplar der Klasse gebunden werden können. Also genau das, was ich mit slots erreichen kann - zumindest solange, wie ich meine Klassen nur von "object" ableite, wie ich inzwischen gelernt habe.
Wozu? Das Properties nützlich sind - klar. Aber wozu willst du das? Zudem ``__slots__`` dazu auch gar nicht gedacht sind.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Leonidas hat geschrieben:Aber wozu willst du das? Zudem ``__slots__`` dazu auch gar nicht gedacht sind.
Für ein Projekt, dass ich mir vorgenommen habe, wäre das aus bestimmten Gründen (das kann ich ein andermal ausführen) sinnvoll.

Nachdem was ich bisher über slots gelesen habe, sind sie genau dafür gedacht (Lutz/Ascher, S. 400f). Hättest du einen Link für mich, wo ich nachlesen kann, wozu sie *wirklich* gedacht sind?

Mein Frage, ob (und ggf. wie) ich das von mir Gewünschte mittels __getattr__ und __setattr__ realisieren kann, ist noch unbeantwortet ...
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

`__slots__` ist dazu gedacht, um Speicher zu sparen, wenn man viele Exemplare erstellt, weil die Exemplare eben kein `__dict__`-Attribute haben.

Und mit `__setattr__` könnte man das so irgendwie machen:

Code: Alles auswählen

class Spam(object):
    def __setattr__(self, name, value):
        if name in ['spam', 'eggs']:
            object.__setattr__(self, name, value)
        else:
            raise AttributeError('invalid attribute')

spam = Spam()
spam.spam = 42
spam.eggs = 'Eggs'
print spam.spam, spam.eggs
spam.foo = 'invalid'
Wobei man das aber einfach umgehen kann (z.B. mit ``spam.__dict__['foo' ] = 'invalid'``).
lunar

pütone hat geschrieben:
Leonidas hat geschrieben:Aber wozu willst du das? Zudem ``__slots__`` dazu auch gar nicht gedacht sind.
Für ein Projekt, dass ich mir vorgenommen habe, wäre das aus bestimmten Gründen (das kann ich ein andermal ausführen) sinnvoll.
Wenn du __slots__ zur Zugriffsbeschränkung verwenden willst/musst, dann hast du entweder Paradigmen aus anderen Sprachen nicht ganz abgelegt, oder ein ziemlich schlechtes Design. Mit dieser Meinung dürfte ich hier nicht alleine stehen...
Nachdem was ich bisher über slots gelesen habe, sind sie genau dafür gedacht (Lutz/Ascher, S. 400f).
Wirf das Buch weg.
Hättest du einen Link für mich, wo ich nachlesen kann, wozu sie *wirklich* gedacht sind?
Das steht gleich am Anfang der offiziellen Dokumentation, die zu lesen sicherlich nicht geschadet hätte.
Mein Frage, ob (und ggf. wie) ich das von mir Gewünschte mittels __getattr__ und __setattr__ realisieren kann, ist noch unbeantwortet ...
Selbst wenn du es nicht wahrhaben willst, aber das was du versuchst, hört sich ziemlich nach dem falschen Ansatz zur Lösung eines wie auch immer gearteten Problems an ;)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@Trundle:
Danke für den Link und das Beispiel; hat beides geholfen.

@lunar:
Mir ist bewusst, dass es gerade eine der Eigenarten von Python ist, so etwas, wie ich es will/wollte, nicht zu tun. Aber ich verstehe nicht, wie du - ohne die Problemstellung zu kennen - beurteilen kannst, dass es "schlechtes Design" oder der "falsche Ansatz" ist.

Die "offizielle Dokumentation" kenne ich auch, d.h. ich weiß, dass es sie gibt. Nein, schaden tut es sicher nicht, sie zu lesen. Das Tutorial habe ich gelesen, die Referenz zu lesen ist für einen Python-Anfänger aber ein hartes Brot. Ich könnte sie mir aber nochmal vornehmen.

In meinem Fall war es ja auch so, dass ich - in einem gedruckten Buch - etwas über slots gelesen und mich darauf gestützt hatte. Ohne Grund gehe ich ja zunächst nicht davon aus, dass das sicher nicht stimmt und ich woanders mich noch vergewissern sollte.
lunar

pütone hat geschrieben:Aber ich verstehe nicht, wie du - ohne die Problemstellung zu kennen - beurteilen kannst, dass es "schlechtes Design" oder der "falsche Ansatz" ist.
Nun, die Verwendung von __slots__ zur Steuerung der Attributzugriffe ist immer der falsche Ansatz, weil __slots__ dafür nicht gedacht ist!

Das Überscheiben von __[gs]etattr__ zur Zugriffssteuerung ist im mindesten fraglich, da "harte" Zugriffsbeschränkung nicht zu den Konzepten von Python passt.

Fazit: Wenn du Zugriffsbeschränkungen nutzen musst, dann ist höchstwahrscheinlich der Ansatz zur Problemlösung verkehrt.
Die "offizielle Dokumentation" kenne ich auch, d.h. ich weiß, dass es sie gibt. Nein, schaden tut es sicher nicht, sie zu lesen. Das Tutorial habe ich gelesen, die Referenz zu lesen ist für einen Python-Anfänger aber ein hartes Brot. Ich könnte sie mir aber nochmal vornehmen.
Der Link von Trundle hat dir offenbar aber geholfen... und das war ein Link zur offiziellen Dokumentation der nächsten Python Version. Ich habe den Link zur aktuellen Doku gepostet, die zwar nicht ganz so schön aussieht, in der aber im Bezug auf __slots__ exakt das gleiche steht.
In meinem Fall war es ja auch so, dass ich - in einem gedruckten Buch - etwas über slots gelesen und mich darauf gestützt hatte. Ohne Grund gehe ich ja zunächst nicht davon aus, dass das sicher nicht stimmt und ich woanders mich noch vergewissern sollte.
Das wäre nicht der erste Fall eines schlechten Python Buchs. Viele Bücher scheinen von fachfremden Autoren geschrieben zu sein, die einfach blind aus anderen Sprachen bekannte Paradigmen auf Python zu übertragen, ohne sich vorher detailliert mit den Konzepten von Python beschäftigt zu haben. Im Falle des Galileo Openbooks sieht man ziemlich deutlich, dass der Autor wohl von Java kommt.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

lunar hat geschrieben:Nun, die Verwendung von __slots__ zur Steuerung der Attributzugriffe ist immer der falsche Ansatz, weil __slots__ dafür nicht gedacht ist!

Das Überscheiben von __[gs]etattr__ zur Zugriffssteuerung ist im mindesten fraglich, da "harte" Zugriffsbeschränkung nicht zu den Konzepten von Python passt.
Ja, das habe ich nun beides - dank eurer Unterstützung - auch verstanden.
lunar hat geschrieben:Fazit: Wenn du Zugriffsbeschränkungen nutzen musst, dann ist höchstwahrscheinlich der Ansatz zur Problemlösung verkehrt.
Ich MUSS sie nicht nutzen in dem Sinne, dass es anders nicht ginge, sondern ich HÄTTE sie GERNE genutzt, weil es im konkreten Fall hilfreich gewesen wäre. Das Hauptaugenmerk lag/liegt darauf, bei versehentlich falsch notierten Attributen eine Fehlermeldung zu bekommen, um die ansonsten in solchen Fällen manchmal schwer aufzufindenden Fehler leichter ausmachen zu können.

Bis gestern nachmittag war ich ja noch davon ausgegangen, dass slots genau zu dem Zweck gedacht sind; jetzt, wo ich schlauer geworden bin und klar ist, dass Python eigentlich bewusst keine Konstruktion vorgesehen hat, um eine dynamische Ergänzung von Attributen zu verhindern, werde ich wohl auch darauf verzichten. Der Weg über __getattr__/__setattr__ hat ja - abgesehen davon, dass es nicht gerade elegant ist - auch den Nachteil, dass die Perfomance bei direkten Attributzugriffen dadurch deutlich leidet.
lunar hat geschrieben:Der Link von Trundle hat dir offenbar aber geholfen... und das war ein Link zur offiziellen Dokumentation der nächsten Python Version. Ich habe den Link zur aktuellen Doku gepostet, die zwar nicht ganz so schön aussieht, in der aber im Bezug auf __slots__ exakt das gleiche steht.

Stimmt alles. Aber Trundle hat es sich verkniffen, den Link durch einen Kommentar mit vorwurfsvollem Unterton zu garnieren ...
lunar hat geschrieben:Das wäre nicht der erste Fall eines schlechten Python Buchs.
Sicher. Ist ja auch in einigen Threads schon ausgiebig erörtert worden. Das von mir zitierte Buch habe ich in einem dieser Threads auch schon selbst schlecht gemacht - allerdings aus anderen Gründen. Dass es - zumindest an diesem Punkt - nachweislich auch inhaltlich daneben liegt, war für mich neu.
lunar

pütone hat geschrieben:Das Hauptaugenmerk lag/liegt darauf, bei versehentlich falsch notierten Attributen eine Fehlermeldung zu bekommen, um die ansonsten in solchen Fällen manchmal schwer aufzufindenden Fehler leichter ausmachen zu können.
Dafür gibt es pylint, pychecker und Unit Tests.
Antworten