Vererbung von private __attribute

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.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Sonntag 11. November 2007, 18:32

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?
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Sonntag 11. November 2007, 18:55

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. :wink:
droptix hat geschrieben:Was tun?
Das Namemangling bleiben lassen.
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Sonntag 11. November 2007, 19:21

Gut, aber das löst nicht mein Problem... Ich will ja nicht erlauben, dass man mittels

Code: Alles auswählen

Y._z = "eggs"
den Wert einfach überschreiben kann. Also was tun?
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Sonntag 11. November 2007, 19:27

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.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 11. November 2007, 23:35

droptix hat geschrieben:Ich will ja nicht erlauben, dass man mittels

Code: Alles auswählen

Y._z = "eggs"
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
>>> 
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Montag 12. November 2007, 09:28

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.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Montag 12. November 2007, 09:29

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.
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Montag 12. November 2007, 10:20

helduel, das stinkt übelst nach Java! ;)
Abgesehen davon kann ich das Property immernoch mit f._Foo__bar o.ä. setzen ;)
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Montag 12. November 2007, 10:44

Hab das "Problem" anders gelöst... nee, Java-Style will ich ja auch nicht :twisted:

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

Montag 12. November 2007, 11:57

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) ...
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Montag 12. November 2007, 12:02

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)
BlackJack

Montag 12. November 2007, 12:03

@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.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Montag 12. November 2007, 13:36

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...
BlackJack

Montag 12. November 2007, 14:06

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.
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Montag 12. November 2007, 15:34

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?
Antworten