Klassen, denen nicht dynamisch Attribute hinzugefügt werden

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
Grueni
User
Beiträge: 5
Registriert: Donnerstag 26. Mai 2011, 09:50

Hallo,

ich programmiere schon seit einiger Zeit in Python (bin aber eher an C++/C# gewöhnt). Allerdings bin ich dabei vor Kurzem auf eine ärgerliche Eigenart gestoßen:

Erstellt man eine Python-Klasse

class dummy
def __init__(self,attr1):
self.a = attr1
def getA(self):
return self.a

und verwendet diese (klar, dafür schreibt man sie ja), dann führen Tippfehler dazu, dass Python ein neues Attribut mit dem verwendeten Namen in der Klasseninstanz anlegt:

d = dummy(3)
d.b = 7
print d.getA()
>> 3

In komplizierteren Fällen kann so ein Verhalten schwer zu erkennen sein (außer an der Fehlerwirkung). Wie kann ich es erreichen, dass stattdessen die Interpretation von
d.b = 7
zu einer Ausnahme führt, die sofort den Fehler erkennt (oder kann der Interpreter entsprechend eingestellt werden?):

d.b=7
>> Traceback ...

Vielen Dank.
Zuletzt geändert von Grueni am Donnerstag 26. Mai 2011, 12:12, insgesamt 1-mal geändert.
BlackJack

@Grueni: Irgendwer wird Dir sicher eine technische Lösung dazu zeigen. Aber wenn Du so etwas machen willst, dann solltest Du IMHO einfach eine andere Programmiersprache verwenden. Wenn das in der Praxis regelmässig Probleme macht, oder Du die Angst es könnte vielleicht welche machen, nicht überwinden kannst, ist Python nichts für Dich.
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Hallo Grueni.

Bitte setze deine Snippets in Zukunft in Code Blöcke, damit sie leichter zu lesen sind.

Zu deiner Frage:
Ich kann dir eine Lösung benennen, allerdings ist die Lösung meines Wissens nicht zu diesem Zweck gedacht. Sie zeigt aber das Verhalten auf, welches du dir wünschst.

Wenn du für eine Klasse __slots__ definierst, legst du damit fest welche Attribute die Klasse hat.
Die Intention für solche Slots ist allerdings minimierung von Speicherverbrauch wenn man viele Objekte einer Klasse hat.
Das man keine neuen Attribute ergänzen kann, ist eine Einschränkung welche __slots__ mitbringen.
Lese dir am besten mal die Doku zu __slots__ durch. (Ist eine Weile her, dass ich das gemacht habe)

Meine Empfehlung wäre es ehr eine statische Codeprüfung wie pylint zu verwenden. Diese würde dir zum Beispiel melden, dass ein Attribut ausserhalb von __init__ zum ersten mal definiert wird. Das wäre kein Fehler, sondern lediglich eine Warning oder Info. Ansonsten helfen noch ein guter Editor mit Codevervollständigung und natürlich unittests.
Grueni
User
Beiträge: 5
Registriert: Donnerstag 26. Mai 2011, 09:50

@BlackJack: Die Programmiersprache kann man sich beruflich ja leider nicht immer aussuchen. Außerdem hat Python ja auch etliche nette Features inclusive einer umfangreichen Standardbibliothek.

@Zap Danke. Das beantwortet meine Frage. Ich werde mal schauen, welche Nebeneffekte __slots__ hat. pylint reicht aber vermutlich aus. Unittests sind selbstverständlich, aber wenn einfache statische Analyse den Fehler findet, ist das natürlich wesentlich sicherer.

Danke!
lunar

@Grueni: Nur, um nochmal explizit darauf hinzuweisen: "__slots__" ist nicht dafür gedacht, die Menge der erlaubten Attribute zu definieren, und es wird in gutem Python-Quelltext auch nicht für diesen Zweck verwendet. In gutem Python-Quelltext gibt es normalerweise überhaupt keine Vorkehrungen, um das Hinzufügen unerwünschter Attribute zu verhindern.

Lasse Dich auf die Eigenarten der Sprache ein, anstatt gegen sie zu programmieren. Auf Dauer ist das leichter, und vermeidet Konflikte mit anderen Python-Entwicklern in Deiner Firma.
Grueni
User
Beiträge: 5
Registriert: Donnerstag 26. Mai 2011, 09:50

@lunar Ja, ich sehe ein, dass Python eine solche Funktionalität nicht vorsieht und die Verwendung von __slots__ ist offensichtlich auch nicht im Sinne von Python.

Ein statischer Syntaxchecker löst zumindest vorerst meine Bedenken. Allerdings verstehe ich eine Sache nicht:
Im Sinne der Objektorientierung, die Python ja mit den Klassen zu implementieren denkt, besteht eine Klasse als Kapselung von Methoden und Attributen. Diese beschreiben das modellierte Objekt vollständig. Eine Erweiterung ist nur über den Vererbungsmechanismus vorgesehen, der ein Objekt mit erweiterter Funktionalität modelliert.

Welchen Sinn erfüllt also das Hinzufügen von Attributen zu einem gekapselten Objekt, außer dass dadurch 'dirty style programming' ermöglicht wird. Gibt es hier sinnvolle Anwendungen?
BlackJack

@Grueni: In Python wird so etwas halt nicht erzwungen. Man geht davon aus das der Programmierer weiss was er tut. Das macht das Ganze viel einfacher und auch flexibler. Es gibt Objekte und denen kann man in der Regel jederzeit Attribute hinzufügen. Und in der Regel macht man so etwas nur innerhalb von `__init__()`-Methoden, wenn man ein verständliches Programm haben möchte. Andererseits kann man es auch in anderen Methoden machen und so zum Beispiel die `__init__()` auf mehrere Methoden verteilen. Oder von Metaklassen oder Klassendekoratoren aus Attribute hinzufügen. Man müsste also einen Mechanismus vorsehen mit dem man eine Klasse "abschliessen" aber auch wieder "öffnen" kann. Das ist Mehrarbeit für Leute die wissen was sie tun, und hält Leute die es nicht wissen, aber auch wieder nicht davon ab Blödsinn zu machen.
lunar

@Grueni: Zusätzlich zu den Anmerkungen von BlackJack noch ein paar Kommentare zur „Objektorientierung“.

Klassen, Kapselung und Vererbung sind Konzepte aus Java, C++ oder C#, auch wenn in der Literatur oder der Lehre oft der Eindruck erweckt wird, diese Konzepte wäre inhärenter Teil der OOP und mithin obligatorisch für eine objektorientierte Sprache.

OOP selbst ist aber nicht mehr als die Modellierung eines Systems aus freilebenden Objekten, die einander Nachrichten schicken können (im Gegensatz zur prozeduralen Programmierung, die Systeme als Folge von Prozeduraufrufen beschreibt, und keine freilebenden Daten kennt). Die kanonische Sprache, die dieses Verständnis von Objektorientierung implementiert, ist Smalltalk, eine der ersten objektorientierten Sprachen und weitaus älter als Java und Co. Klassen oder Zugriffsschutz spielen in dieser Deutung gar keine Rolle, es gibt objektorientierte Sprache ohne Klassen (e.g. IO oder Javascript).

Fazit ist, dass jede Sprache ein eigenes Verständnis von Objektorientierung implementiert, und mithin auch andere, mitunter ganz eigene Konzepte umsetzt, und dass man auch ohne spezielle syntaktische Unterstützung seitens der Sprache objektorientiert programmieren kann (sogar in C).

Blende also am besten alles aus, was Du über Objektorientierung aus Java oder ähnlichen Sprachen weißt, und versuche einfach nur, guten Python-Quelltext zu schreiben. Dieser Quelltext muss Dir nicht gefallen, aber ist es immer leichter, mit der Sprache zu arbeiten als gegen sie.
Grueni
User
Beiträge: 5
Registriert: Donnerstag 26. Mai 2011, 09:50

Ich habe nichts gegen diese Definition von Objektorientierung. Trotz allem ist Kapselung nicht der wesentliche Punkt, den ich am Klassenkonzept zu bemängeln habe. Es ging eher um die Idee hinter OOP, dass ein reales oder abstraktes Objekt vollständig durch eine Klasse (oder wie immer man es nennen möchte. Vernünftige C-Module tun es hier natürlich auch) modelliert wird, so dass eine spätere Erweiterung einfach nicht mehr notwendig ist.

In diesem Sinne stellt eine solche Funktionalität in Python kein Problem für mich dar, da ich sie so nicht nutzen möchte. Das Problem liegt eher darin, dass dieses Konzept die Fehlerwahrscheinlichkeit im Code erhöht. Danke also, dass ihr mir gezeigt habt, dass es einen Grund gibt, dies in Kauf zu nehmen.

Der Programmierer weiss immer was er tut, auch wenn der Code dann hinterher nicht macht, was er will ;-) Es geht mir eher darum, dass einmal geschriebener Code auch wartbar ist von jemandem, der vielleicht nicht die Eigenarten des ursprünglichen Entwicklers kannte.

Ich denke aber, dass ich mich mit entsprechenden Tools durchaus mit Python angefreundet habe, solange der Code sinnvoll dokumentiert ist. Ist ja auch nur eine weitere Sprache, die sich nicht so wesentlich von den anderen unterscheidet, und keine Religion. Mir persönlich sagen die typsicheren Sprachen trotzdem eher zu als Python (ja, man kann Typtests machen, aber das entspricht ja auch eher nicht dem Sprachkonzept, sonst wären die gleich drin), aber das liegt wohl daran, dass ich damit aufgewachsen bin ;-)
lunar

@Grueni: Das ist nicht meine eigene Definition von Objektorientierung, sondern die von Alan Kay.
Grueni
User
Beiträge: 5
Registriert: Donnerstag 26. Mai 2011, 09:50

@lunar Entschuldige. Ich meinte eine Definition von Python, in der sich diese Sprache von den meisten gängigen Sprachen unterscheidet. Ich habe verstanden, dass die von dir genannte Definition die Lehrmeinung ist und ich mich in der eingeschränkten Version geirrt habe. Trotzdem wird Kapselung meist als wichtige Eigenschaft von "guten" Klassen genannt.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Python ist übrigens typsicher, du kannst z.B. nicht

Code: Alles auswählen

a = '4' + 4
schreiben.
Es ist halt nicht sicher, dass du den Typ geliefert bekommst, den du erwartest. :)
BlackJack

@Grueni: Ich denke hier spielt so ein bisschen mit rein, dass viele Leute, die von statischen Sprachen wie C++, Java, C#, … kommen IMHO fälschlicherweise Kapselung mit erzwungenem Zugriffsschutz gleichsetzen. Kapselung wird auch in der ”Python-Kultur” als nützliches Konzept angesehen. Nur wird es eben nicht erzwungen. Der Zugriff auf Attribute oder das Hinzufügen von Attributen ist normalerweise erlaubt, aber wenn Dokumentation oder Namenskonvention sagen, dass ein Attribut ein Implementierungsdetail ist, dann greift man nicht darauf zu. Es sei denn man kennt die Implementierung und weiss was man tut. Das gleiche gilt für das hinzufügen von Attributen — macht man in aller Regel nur innerhalb der `__init__()`.

Da Du in dem Zusammenhang von Bedenken gesprochen hast: Solche Fehler werden eigentlich von Unittests gefunden oder fallen anderweitig schnell auf. So ein leichtes Unsicherheitsgefühl gibt es öfter bei Leuten, die von statischer geprüften Sprachen kommen und denken „Oh das ist ja alles so lax hier, das ist sicher gefährlich” und zu deren Erstaunen der Weltuntergang dann doch ausbleibt, der von Verfechtern von statischer Typisierung so gerne an die Wand gemalt wird, wenn der Compiler einen nicht mehr vor den ganzen Fehlern warnen kann, die man wohl ständig machen muss, wenn er das nicht täte. :-)
lunar

@mkesper: Das Beispiel demonstriert eher die „Sicherheit“ der eingebauten Typen, nicht die Typsicherheit der Sprache selbst.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Es war eine bewusste Entscheidung von Python, dass Exemplar-Objekte und Klassen-Objekte beliebige Eigenschaften haben können. JavaScript macht's genau so. Kann man gut finden, oder auch nicht. Es ist aber nicht falsch oder richtig. In der Praxis ist der theoretisch vorkommende Fehlerfall, dass man sich beim Setzen eines Eigenschaftswerts verschreibt, selten und zudem leicht mit Werkzeugunterstützung zu finden. Wenn man denn nicht getattr() und setattr() benutzt. Durch statische Analyse kann eine gute IDE (z.B. Jetbrains PyCharm) leicht erkennen, dass man zwar eine Eigenschaft setzt, aber nie liest. Das ist in der Regel ein Fehler und eine Warnung wert. Auch zur Laufzeit fällt so etwas schnell auf, denn die Eigenschaft, die man ja eigentlich setzten wollte existiert nun nicht oder hat den Wert None, wodurch es in der Regel kracht.

Klassen sind übrigens eine recht späte Erfindung der Objektorientierung, die ersten Smalltalk-Dialekte hatte noch keine. Klassen sind zwar älter und stammen aus Simula, doch da gab es die Idee der Objektorientierung noch nicht (sie wurde von schon erwähntem Alan Kay erfunden) und somit "gildet" das IMHO nicht. Die wahrscheinlich reinste OO-Sprache, Self, hat daher konsequenterweise auf Klassen verzichtet und JavaScript hat dieses Konzept von Self übernommen. Leider ist der Wunsch, alles zu klassifizieren statt in Beispielen zu Denken, etwas, dass vielleicht nicht zutiefst menschlich ist, aber dann doch vielen Software-Entwicklern antrainiert wird. Dabei wäre das Denken in Beispielen (Prototypen) eigentlich viel einfacher... finde ich.

Wat bringt uns das nun für Python? Gar nix. Glücklicherweise sind hier Klassen und Exemplare von Klassen nicht zwingend notwendig und man kann davon so viel benutzen wie man will und ansonsten noch Funktionen benutzen und das fehlende statische Typkorsett vermissen und als Schwäche monieren oder die Abwesenheit als Befreiung feiern. Die Wahrheit liegt irgendwo dazwischen.

Bei Smalltalk muss man übrigens die Liste der Exemplarvariablen vordefinieren und kann auch von außen nicht auf diese Zugreifen, sondern muss redundant immer getter- und setter-Methoden anlegen, aber das war eine Entscheidung aus Effizienzgründen, denn aus Gründen der Typsicherheit.

Stefan
Antworten