AttributeError? Wieso denn?

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hallo da,

wollte eine My_Rect Klasse machen, die von sich aus auch x und y floats stored als Attribute. Bekomme da einen komischen Fehler...

Code: Alles auswählen

class My_Rect(Rect):

	def __init__(self, rect, offsets=(0,0)):
		Rect.__init__(self, rect)
		self.offsets = offsets
		print offsets
		print self.offsets
		self._init_pos_()
	def _init_pos_(self):
		self.x = float(self.left + self.offsets[0])
		self.y = float(self.top + self.offsets[1])
	def __setattr__(self, key, value):
		if key == "x":
			self.x = value
			self.topleft = (int(self.x) - self.offsets[0], int(self.y) - self.offsets[1])
		elif key == "y":
			self.y = value
			self.topleft = (int(self.x) - self.offsets[0], int(self.y) - self.offsets[1])
Das spuckt aus:

Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
nu = My_Rect(ni)
File "<pyshell#45>", line 7, in __init__
print self.offsets
AttributeError: 'My_Rect' object has no attribute 'offsets'

Wieso wird self.offsets nicht zugewiesen da?
Ich code, also bin ich.
BlackJack

@Henry Jones Jr.: Sieht in der Tat interessant aus. Ich tippe mal darauf das `Rect` eine `__new__()`-Methode implementiert.
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hui, was ist das denn genau? Kann ich das umgehen oder muss ich eine komplett eigene Rect Klasse machen, wenn ich x und y als float Attribute haben möchte?
Ich code, also bin ich.
BlackJack

@Henry Jones Jr.: Okay, Kommando zurück. Überleg doch mal wann die `__setattr__()`-Methode aufgerufen wird. Es hilft beim Verständnis wenn Du einen ``else``-Zweig hinzufügst bei dem `key` ausgegeben wird. :-)

Und überhaupt: Wenn `__setattr__()` mit 'x' als Schlüssel aufgerufen wird und Du daraufhin in der Methode ``self.x = …`` schreibst, dann wird doch *wieder* `__setattr__()` aufgerufen, was dann ``self.x = …`` enthält und wieder `__setattr__()` aufruft, was wieder…
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ergaenzend zu BlackJacks letztem Satz: Du willst `x` und `y` eigentlich mit einem `property` abdecken.
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Danke mal für die guten Tipps! Wenn ich also "intelligente" x und y Attribute haben möchte bei meinem Rect, ist __setattr__ wohl der falsche Weg; das weist ja einfach dem Attribut einen Wert zu? Also self.x = 99 ruft auf self.__setattr__("x", 99)? Soweit ich das verstanden habe.

Da wäre nun wieder dieses property; ich cheggs noch immer nicht :( Habe das gefunden z.Bsp.:

Code: Alles auswählen

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x
Erst mal verstehe ich nicht, warum oben self._x geschrieben wird. Was soll der eine Underline? Kann ich auch ohne? Was ich bisher zum Underline herausgefunden habe. ist, dass wenn ich innerhalb der __init__ Methode bereits eine andere Methode aufrufen möchte, ich diese mit def _shrubbery_() definieren muss, also mit Underlines. Aber bei meinem Rect ist doch self.x self.x und basta, oder wie würdet ihr?

Und dann zum property... das Attribut im Beispiel heisst ja eben self._x, unten aber dann im property Zeugs ist's wieder ohne Underline und dann wieder mit... Ich hab keine Ahnung was das soll. Was hier "@x.deleter" und "@x.setter" soll glaube ich verstehe ich und das glaube ich ist genau was ich suche! Aber das erste @property?? Was tut das? Gibt es eine "noch strikter pythonische" Schreibweise für das ohne "@" oder muss ich mich daran gewöhnen? Könnt ihr mir auf die Sprünge helfen?
Ich code, also bin ich.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

»self.x« und »self._x« sind ja gerade nicht das selbe, deshalb müssen sie ja auch verschiedene Namen haben. »self.x« ist die öffentliche API, das Property, »self._x« ist ein internes Attribut. Das @ zeigt an, dass »property« ein Dekorator ist, die nachfolgende Methode also Erweitert, hier zu einem Property macht.
Das Beispiel ist natürlich völlig sinnfrei, weil man »self.x« auch direkt als Attribut schreiben könnte.
Zu Deinem Anwendungsfall:

Code: Alles auswählen

class My_Rect(Rect):
    def __init__(self, rect, offsets=(0,0)):
        Rect.__init__(self, rect)
        self.offsets = offsets

    @property
    def x(self):
        return float(self.left + self.offsets[0])

    @x.setter
    def x(self, value):
        self.left = int(value) - self.offset[0]
BlackJack

@Henry Jones Jr.: Das Attribut hat den Namen `_x` weil das Property ja schon den Namen `x` hat. Ein führender Unterstrich ist ausserdem die Namenskonvention für Attribute auf die der Benutzer der Klasse nicht von aussen zugreifen sollte. Braucht er ja auch nicht, denn dafür gibt es ja das Attribut/Property `x`. Das mit `_shrubbery_()` ist Unsinn. Es kann sinnvoll sein in der `__init__()` Methoden aufzurufen die nicht zur öffentlichen API des Datentyps gehören, also mit einem Unterstrich beginnen, es kann aber genau so sinnvoll sein öffentliche Methoden aufzurufen. Und der abschliessende Unterstrich macht keinen Sinn. Das einzige was mir da so auf Anhieb einfällt ist das `_fields_`-Attribut von `ctypes`, wo die Unterstriche wohl so etwas wie „fast so magisch wie die magischen Namen von Python selbst” nahelegen sollen.

Die erste Methode `x()` wird aufgerufen wenn jemand `c.x` schreibt, die zweite wenn man `c.x = 42` scheibt und die dritte bei `del c.x`. Immer unter der Vorgabe das `c` an ein Exemplar vom Typ `C` gebunden ist.

Die Dekoratorsyntax *ist* „pythonisch” und wurde extra eingeführt weil die Alternative unschöner ist. Das wäre:

Code: Alles auswählen

class C(object):
    def __init__(self):
        self._x = None
 
    def _get_x(self):
        return self._x
 
    def _set_x(self, value):
        self._x = value
 
    def _del_x(self):
        del self._x

    x = property(_get_x, _set_x, _del_x, """I'm the 'x' property.""")
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

es knirscht und knackt in meinem Kopf... da tut sich was, danke! Ich probier mal bischn rum damit ich damit sattelfest werde

danke!
Ich code, also bin ich.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Der führende Unterstrich ist lediglich eine Konvention (Coding-Style), welche besagt: "Fass mich nicht an, ich gehöre nicht zur öffentlichen Schnittstelle dieser Klasse. Mein Verhalten ist (nach außen) nicht garantiert."

In Deinem Beispiel ist das Attribut x (ja, auch Methoden sind Attribute) die öffentliche Schnittstelle.

Wenn du genaueres zum @ wissen willst, suche nach dem Stichwort "Dekorator". Ist im Grunde nichts wildes, wenn man es mal verstanden hat. Ein Dekorator ist eine Funktion, welche eine (andere) Funktion als Argument entgegen nimmt und eine Funktion als Ergebnis zurück gibt.

Was Dein Ausgangsproblem angeht: Erbe nicht von pygame.Rect! pygame ist ein Wrapper um eine c-Bibliothek (libSDL). SDL_Rect ist ein C-Struct, welcher IIRC nur mit Integer kann ;). Wobei ich mir die Source zu pygame.Rect nicht genau angesehen habe, bei *Proxy habe ich schon abgeschaltet :?

Schreibe Dir Deine eigene Rect-Klasse (so wie ich es auch vor Kurzem noch getan habe) und eine freie Funktion, welche aus Deinem Rect ein pygame.Rect zusammenschraubt.

PS.: BlackJack ist (wie immer) schneller :D
PPS.: Der OP auch ...
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

bwbg hat geschrieben:
Schreibe Dir Deine eigene Rect-Klasse (so wie ich es auch vor Kurzem noch getan habe) und eine freie Funktion, welche aus Deinem Rect ein pygame.Rect zusammenschraubt.
Fett, danke!

Genau das tu ich so tun jetz, jawoll! Gute Übung nebenbei :P
Ich code, also bin ich.
Antworten