Seite 1 von 1
Vererbung von private __attribute
Verfasst: Sonntag 11. November 2007, 18:32
von droptix
Hum komisch, habe eine BaseClass mit einem Attribut mit zwei führenden Unterstrichen. In der Klasse, die von BaseClass erbt, ist dieses Attribut nicht verfügbar. Ich möchte das Attribut aber als "private" deklarieren.
Code: Alles auswählen
class A:
__c = ""
def __init__(self):
print "calling A.__init__()..."
self.__c = "OK"
self.start()
def start(self):
# overwrite me
pass
class B(A):
def start(self):
print self.__c
B()
Ergebnis:
calling A.__init__()...
Traceback (most recent call last):
File "inherit_ab.py", line 16, in ?
B()
File "inherit_ab.py", line 6, in __init__
self.start()
File "inherit_ab.py", line 14, in start
print self.__c
AttributeError: B instance has no attribute '_B__c'
Mit einfachem Unterstrich geht's, aber dann lässt sich das Attribut "von extern" auch überschreiben (was ich nicht erlauben möchte):
Code: Alles auswählen
class X:
_z = ""
def __init__(self):
print "calling X.__init__()..."
self._z = "OK"
self.start()
def start(self):
# overwrite me
pass
class Y(X):
def start(self):
print self._z
Y()
Ergebnis:
calling X.__init__()...
OK
Was tun?
Verfasst: Sonntag 11. November 2007, 18:55
von veers
Ich denke du hast einige Konzepte von Python noch nicht verstanden.
Aber um zu deiner Frage zu kommen: Eine private Methode kann von einer Subklasse per Definition nicht aufgerufen werden. Namemangling (sprich __name, das zu _Klasse__name umgewandelt wird) sollte eigentlich auch nur dazu verwendet werden um Kollisionen zu verhindern. Nicht um die Öffentliche von der Privaten Schnittstelle zu unterscheiden.
droptix hat geschrieben:Was tun?
Das Namemangling bleiben lassen.
Verfasst: Sonntag 11. November 2007, 19:21
von droptix
Gut, aber das löst nicht mein Problem... Ich will ja nicht erlauben, dass man mittels
den Wert einfach überschreiben kann. Also was tun?
Verfasst: Sonntag 11. November 2007, 19:27
von Zap
Ein Pythonprogrammierer weiß an der Stelle das er Attribute mit führendem Unterstrich möglichst nicht anfassen soll.
Man kann so oder so an alles dran (Es gibt immer Wege)
Diese "ich will das Unterbinden" Thematik wurde hier schon öfter behandelt immer mit dem selben Ergebnis:
Es geht einfach nicht es wirklich zu unterbinden, es gibt lediglich diese Konvention.
Verfasst: Sonntag 11. November 2007, 23:35
von Leonidas
droptix hat geschrieben:Ich will ja nicht erlauben, dass man mittels
den Wert einfach überschreiben kann. Also was tun?
Das `__setattr__`der Klasse überschreiben, dass es Exceptions wirft, wenn du versuchst etwas zu ändern. Zusätzlich noch `__all__` deklarieren.
Und ja: `private` braucht man in Python nicht. Wir sind nicht Java-Programmierer.
Edit: Oha, meine Idee funktioniert ja tatsächlich:
Code: Alles auswählen
>>> class Restrict(object):
... def __setattr__(self, name, value):
... raise NotImplementedError('you suck')
...
>>> r = Restrict()
>>> r
<__main__.Restrict object at 0x2b43c592b890>
>>> r.a = 3
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in __setattr__
NotImplementedError: you suck
>>>
Verfasst: Montag 12. November 2007, 09:28
von Rebecca
Leonidas, so kann man den Wert gar nicht mehr aendern, weder von aussen noch von innen. Man koennte auch eine Property mit entsprechender Setter-Funktion schreiben, das laeuft aufs gleiche hinaus.
Verfasst: Montag 12. November 2007, 09:29
von helduel
Moin,
kannst dir auch mal die Properties anschauen. Vielleicht kannst du damit das erreichen, was du willst.
Code: Alles auswählen
class Foo(object):
def __init__(self):
self.__bar = None
@property
def bar(self):
return self.__bar
f = Foo()
f.bar = 'test' # AttributeError
So kann jeder die Variable auslesen, aber nicht beschreiben - zumindest nicht ohne Weiteres. Wenn man sie aber auch von Außen beeinflussen können soll, nur eben kontrolliert, dann kannst du auch einen setter schreiben und wieder mit Properties arbeiten.
Code: Alles auswählen
class Foo(object):
def __init__(self):
self.__bar = None
def get_bar(self):
return self.__bar
def set_bar(self, value):
if not value:
raise ValueError, 'so net!'
self.__bar = value
bar = property(get_bar, set_bar)
f = Foo()
f.bar = None # ValueError
b.bar = 'geht'
Eine Stufe härter geht's mit Deskriptoren. Aber das mag ein Overkill sein.
Verfasst: Montag 12. November 2007, 10:20
von veers
helduel, das stinkt übelst nach Java!

Abgesehen davon kann ich das Property immernoch mit f._Foo__bar o.ä. setzen

Idee dafür
Verfasst: Montag 12. November 2007, 10:44
von droptix
Hab das "Problem" anders gelöst... nee, Java-Style will ich ja auch nicht
Hab eine Methode `get_myattribute()` in der BaseClass, die den Wert des Attributs zurück liefert, um damit "lesend" arbeiten zu können... weiß nicht wie ich das jetzt anders ausdrücken soll... Seltsamerweise funzt das.
Edit: hier der veränderte Code:
Code: Alles auswählen
class A:
__c = ""
def __init__(self):
print "calling A.__init__()..."
self.__c = "OK"
self.start()
def start(self):
# overwrite me
pass
def get_c(self):
return self.__c
class B(A):
def start(self):
print self.get_c()
B()
Finde es trotzdem komisch, dass das Attribut nicht vererbt wird. Wieso nicht?
Man könnte vielleicht mittels `super(MyClass)` die BaseClass ermitteln und alle Attribute mit "double quoted underscore" von dort über `__init__()` in der SubClass verfügbar machen. Gar nicht so uninteressant... hätte jemand ein funktionierendes Code-Snippet dafür?
Verfasst: Montag 12. November 2007, 11:57
von Jona
veers hat geschrieben:Ich denke du hast einige Konzepte von Python noch nicht verstanden.
Aber um zu deiner Frage zu kommen: Eine private Methode kann von einer Subklasse per Definition nicht aufgerufen werden. Namemangling (sprich __name, das zu _Klasse__name umgewandelt wird) ...
Re: Idee dafür
Verfasst: Montag 12. November 2007, 12:02
von keppla
Jona hat recht, und um mal einen etwas ausführlicheren Tip zu geben: Attribute, auf die nur Subklassen zugreifen können, sind nicht "private", sondern "protected". Private heist "diese Klasse, und sonst wirklich niemand*"
Kannst ja mal in Java, C oder sonstirgendeiner Sprache, die sowas erzwingt, etwas rumexperimentieren.
* (der nicht gemein genug ist, reflection oder ähnliche Tricks zu nutzen)
Verfasst: Montag 12. November 2007, 12:03
von BlackJack
@droptix: Wieso findest Du das komisch? Die doppelten Unterstriche sind ausdrücklich dafür da, dass man den Namen in Unterklassen nicht sieht um Namenskollisionen zu vermeiden. Das ist der Sinn und Zweck davon! Das irgendwie umgehen zu wollen ist krank.
Wenn Du kein Java in Python programmieren möchtest, ersetze die doppelten Unterstriche durch einen einfachen und werde die `get_*`-Methode(n) los. Entweder durch ein Property ersetzen, oder falls es wirklich nur darum geht das Ding "read only" zu machen, werde die Unterstriche und "getter" komplett los und dokumentiere, dass das Attribut nur gelesen werden soll.
Verfasst: Montag 12. November 2007, 13:36
von droptix
keppla hat geschrieben:Jona hat recht, und um mal einen etwas ausführlicheren Tip zu geben: Attribute, auf die nur Subklassen zugreifen können, sind nicht "private", sondern "protected".
Stimmt, "protected" wäre das richtige Wort gewesen.
BlackJack hat geschrieben:@droptix: Wieso findest Du das komisch? Die doppelten Unterstriche sind ausdrücklich dafür da, dass man den Namen in Unterklassen nicht sieht um Namenskollisionen zu vermeiden. Das ist der Sinn und Zweck davon! Das irgendwie umgehen zu wollen ist krank.
Naja nicht gleich "krank"... in anderen Programmiersprachen gibt es eben "public", "private" und "protected"... Python bietet vergleichbar zwei dieser drei Arten an, aber "protected" fehlt eben (leider).
Ich fände es ganz sinnvoll... Namenskollisionen könnte man ja trotzdem vermeiden; man müsste nur zwischen "protected" und "private" unterscheiden können... damit liegt die Absicht beim Programmierer.
Was ich aber eigentlich komisch finde ist, dass die SubClass auf `__blah` zugreifen kann, allerdings nur mit Hilfe der BaseClass. Das Attribut ist also auch in der SubClass da, nur eben nicht "direkt". Dort hab ich ein kleines Verständnisproblem, wieso man nicht auch den anderen Fall à la "protected" eingeführt hat.
Irgendwo zu dokumentieren, dass das Attribut nur lesend zu benutzen ist ist zu unsicher -> ihr kennt euch ja selber wie genau ihr solche Hinweise lest und beachtet. Das Attribut könnte aus Versehen manipuliert werden und am Ende das Programm kompromittieren, was ich als Programmierer weitestgehend verhindern möchte. Klar kann man auch "brutal" mittels `_Class__attribute` Mist machen, aber das ist schon eine andere Einstellung zur Herangehensweise. Sicher ist nichts, wenn der Quellcode zugänglich ist...
Verfasst: Montag 12. November 2007, 14:06
von BlackJack
Doch es ist krank. Doppelte Unterstriche sind dazu da das diese Namen in Unterklassen nicht sichtbar sind. Sich dann Wege auszudenken das mit Hacks die Namen in Unterklassen, und zwar alle, doch wieder sichtbar sind ist total bescheuert. Dann hätte man sie ja einfach nur nicht "unsichtbar" machen brauchen.
Doppelte Unterstriche sollten nur verwendet werden um Namenskollisionen zu vermeiden, nicht um "private" aus anderen Sprachen nachzubilden. Habe ich den ganzen Jahren höchsten drei oder viermal gebraucht. In den meisten Fällen bei Mixins weil man da ja nicht weiss, was für Attribute am Ende noch so in der Klasse landen. "private" und "protected" ist in Python *ein* führender Unterstrich und Programmierer die wissen was sie tun. Wer die Doku nicht liest hat Pech gehabt und wird das eben auf die harte Tour herausfinden müssen. Spätestens im Testcode sollte das ja auch auffallen.
Wenn Dir das zu unsicher ist, dann ist Python vielleicht nicht die richtige Sprache für Dich.
Verfasst: Montag 12. November 2007, 15:34
von keppla
Naja nicht gleich "krank"... in anderen Programmiersprachen gibt es eben "public", "private" und "protected"... Python bietet vergleichbar zwei dieser drei Arten an, aber "protected" fehlt eben (leider).
Andere Programmiersprachen bieten noch viel, viel mehr an.
C++ hat "friend", mit dem man anderen erlauben kann, an den eigenen private parts rumzuspielen (warum da friend und nicht lover genommen wurde, wird sich mir nie erschliessen

)
Java hat noch "default", was auf "jeder in meinem package und alle, die auch protected dürfen" hinausläuft.
Der Punkt ist aber, wozu du das nutzen willst. Bei C++ kannst du alles per Pointer oder Cast umgehen, du kannst also nicht das Programm von der Programmierern, sondern nur die Programmierer vor sich selbst schützen.
Bei Java hängt da afaik auch ein gewisses Sandboxing mit dran (applets & co), was aber in einer skriptsprache wie python nicht Sinnvoll zu realisieren ist.
Bliebe also der "wir schützen die Programmierer vor sich selbst"-ansatz.
Da bliebe zu sagen: Wer entgegen der ausdrücklichen Warnung (fass nichts an, was mit _ beginnt) Werte nutzt, ist auch durch härtere Maßnahmen nicht vor sich selbst zu schützen.
Ich fände es ganz sinnvoll... Namenskollisionen könnte man ja trotzdem vermeiden; man müsste nur zwischen "protected" und "private" unterscheiden können... damit liegt die Absicht beim Programmierer.
Man kann zwischen Protected und Private unterscheiden, wenn du so willst.
__x ist private, man kommt nur über unsportliche umwege dran.
_x ist protected/default, jeder, der es anfasst, muss wissen, was er tut.
alles andere ist Public.
Irgendwo zu dokumentieren, dass das Attribut nur lesend zu benutzen ist ist zu unsicher -> ihr kennt euch ja selber wie genau ihr solche Hinweise lest und beachtet.
Es bedarf keiner Hinweise. "_" ist Hinweis genug. Ich kann mir keinen fall vorstellen, wo jemand da etwas "Aus versehen manipulieren" kann.
Klar kann man auch "brutal" mittels `_Class__attribute` Mist machen, aber das ist schon eine andere Einstellung zur Herangehensweise. Sicher ist nichts, wenn der Quellcode zugänglich ist...
Könntest du diese dubiose Statement bitte etwas erläutern?
Verfasst: Montag 12. November 2007, 16:22
von Leonidas
Rebecca hat geschrieben:Leonidas, so kann man den Wert gar nicht mehr aendern, weder von aussen noch von innen.
Man müsste im __getattr__ den Stackframe zur Hand nehmen und gucken wo der Aufruf her kam. Ist, denke ich, durchaus zu schaffen, nicht einmal sonderlich kompliziert.
Verfasst: Montag 12. November 2007, 20:35
von droptix
Nun gut, ich gebe mich nach reichlicher Überlegung mit dem einfachen Unterstrich zufrieden. Wahrscheinlich habt ihr einfach recht

Und danke dass ihr mir das nochmal vor Augen gehalten habt.
Verfasst: Dienstag 13. November 2007, 02:10
von veers
droptix hat geschrieben:Irgendwo zu dokumentieren, dass das Attribut nur lesend zu benutzen ist ist zu unsicher -> ihr kennt euch ja selber wie genau ihr solche Hinweise lest und beachtet.
Wer die Dokumentation nicht liest ist selber schuld.
Ich denke das ist eines der Design Prinzipien von Java: Programmierer sind "blöd" und müssen so stark eingeschränkt werden wie möglich.
Python ist da viel offener, und geht davon aus dass der Programmierer am besten weiss warum er etwas tut. Und es scheint in vielen Fällen sehr gut zu funktionieren
droptix hat geschrieben:Das Attribut könnte aus Versehen manipuliert werden und am Ende das Programm kompromittieren, was ich als Programmierer weitestgehend verhindern möchte.
Wie wäre es mit Unittests?
droptix hat geschrieben:Sicher ist nichts,
Vielleicht.
droptix hat geschrieben:wenn der Quellcode zugänglich ist...
In dem Moment, in dem du dein Programm sicher zu machen versuchst, in dem du Quellcode versteckst, entscheidest du bereits das dein Programm unsicher sein wird. Man kann Programme durchaus auch ohne deren Quellcode analysieren und oft auch manipulieren
