Seite 1 von 2

Kombination von newstyle-classes und oldstyle-classes

Verfasst: Samstag 22. März 2008, 19:23
von numerix
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?

Verfasst: Samstag 22. März 2008, 19:28
von Trundle
Von der "alten" Klasse und `object` erben.

Verfasst: Samstag 22. März 2008, 19:53
von numerix
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 ...

Verfasst: Samstag 22. März 2008, 20:05
von Leonidas
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?

Verfasst: Samstag 22. März 2008, 20:13
von BlackJack
Und was heisst `__slots__` funktionieren nicht? Das ist nicht vererbbar, eine Unterklasse hat wieder ein `__dict__`.

Verfasst: Samstag 22. März 2008, 21:23
von numerix
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.

Verfasst: Samstag 22. März 2008, 21:50
von Trundle
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

Verfasst: Samstag 22. März 2008, 22:02
von numerix
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?

Verfasst: Samstag 22. März 2008, 22:32
von 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

Verfasst: Samstag 22. März 2008, 22:49
von numerix
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.

Verfasst: Samstag 22. März 2008, 23:05
von Trundle
Was willst du denn genau erreichen?

Verfasst: Samstag 22. März 2008, 23:21
von numerix
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.

Verfasst: Samstag 22. März 2008, 23:28
von Leonidas
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.

Verfasst: Samstag 22. März 2008, 23:40
von numerix
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 ...

Verfasst: Samstag 22. März 2008, 23:52
von Trundle
`__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'``).

Verfasst: Sonntag 23. März 2008, 00:19
von 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 ;)

Verfasst: Sonntag 23. März 2008, 12:56
von numerix
@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.

Verfasst: Sonntag 23. März 2008, 13:23
von 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.

Verfasst: Sonntag 23. März 2008, 13:55
von numerix
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.

Verfasst: Sonntag 23. März 2008, 14:22
von 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.