Wie der Titel schon sagt, würde mich aus aktuellem Anlass der unterschied zwischen x+= und x=x+ interessieren. Ich habe das als ich mit Python angefangen habe für einfache Beispiele irgendwo gelesen, dass es sich dabei um äquivalente Schreibweisen handlet und seit dem nicht mehr hinterfragt. Jetzt musste ich feststellen, dass dem nicht so ist. Finde aber über google nichts...
Gruß,
Bobby
unterschied von x+= und x=x+
Wie kommst Du darauf, dass dem nicht so ist...? Hast Du ein Beispiel?
Laut Doku zur '__iadd__'-Methode wird auf ein Klassenobjekt mit '__iadd__'-Methode (+=) eben diese aufgerufen, wenn selbige nicht definiert ist, wird auf '__add__' (+) zurückgegriffen. Vereinfacht ausgedrückt.
Demnach sollte zwischen 'x += 5' und 'x = x + 5' kein vordergründiger Unterschied bestehen...
mutetella
Laut Doku zur '__iadd__'-Methode wird auf ein Klassenobjekt mit '__iadd__'-Methode (+=) eben diese aufgerufen, wenn selbige nicht definiert ist, wird auf '__add__' (+) zurückgegriffen. Vereinfacht ausgedrückt.
Demnach sollte zwischen 'x += 5' und 'x = x + 5' kein vordergründiger Unterschied bestehen...
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)
Wenn x eine Instanz einer eigenen definierten Klasse ist, kann zwischen x.__add__ und x.__iadd__ natürlich ein Unterschied wie zwischen Tag und Nacht bestehen. Es ist sogar möglich (wenn auch davon abgeraten wird), dass beide Methoden Seiteneffekte hervorrufen, also zum Beispiel Dateien bearbeiten, Datenbanken manipulieren etc.. Was bei x+=5 und x=x+5 passiert, hängt also davon ab, wie x.__iadd__ bzw. x__add__ definiert sind. Wenn x ein int-Objekt ist, macht es keinen offensichtlichen Unterschied (intern passiert im Interpreter etwas anderes, Optimierungen unterschieden sich und so weiter, aber das ist für die meisten uninteressant).
-
Bobby-John-Joe
- User
- Beiträge: 7
- Registriert: Freitag 23. September 2011, 19:50
Danke erst mal für die antworten, evtl schau ich später noch ob ich ein minibeispiel zusammen bekomme, welches den selben 'Fehler hat'.
Aufgefallen ist es mir in folgender Situation:
Ich bin gerade dabei ein Programm zum Kartenspielen zu schreiben. Dabei hat die Klasse Spieler das Attribut 'handkarten', also eine liste der Eigenen Karten. Das Hauptprogramm erzeugt dann 4 Spieler, mischt die Karten und Verteilt diese (übergibt also eine Liste mit 8 Karten an Jeden Spieler).
Nachdem es später die Möglichkeit geben soll auch 2 mal 4 Karten zu geben muss ich die Liste der neuen übergeben Karten an 'handkarten' anhängen (könnten ja schon Karten auf der Hand sein). Das habe ich zunächst so gemacht:
Das hat dann dazu geführt, dass nach dem Austeilen ALLE Spieler 32 Karten hatten. Keine Ahnung wieso, aber mit dem Aufruf
wurde zwar nicht die Methode 'aufenehmen' der anderen Spieler aufgerufen (soweit so gut), aber das jeweilige (private) Attribut __handkarten wurde gesetzt... (wie auch immer)
Schreibe ich hingegen:
dann läuft alles wie gewollt und jeder bekommt 8 individuelle Karten.
Ich schau mal ob ich ein minibeispiel hinbekomme...
Ging recht fix:
Aufgefallen ist es mir in folgender Situation:
Ich bin gerade dabei ein Programm zum Kartenspielen zu schreiben. Dabei hat die Klasse Spieler das Attribut 'handkarten', also eine liste der Eigenen Karten. Das Hauptprogramm erzeugt dann 4 Spieler, mischt die Karten und Verteilt diese (übergibt also eine Liste mit 8 Karten an Jeden Spieler).
Nachdem es später die Möglichkeit geben soll auch 2 mal 4 Karten zu geben muss ich die Liste der neuen übergeben Karten an 'handkarten' anhängen (könnten ja schon Karten auf der Hand sein). Das habe ich zunächst so gemacht:
Code: Alles auswählen
def aufnehmen(self,karten):
self.__handkarten+=karten
Code: Alles auswählen
Spieler1.aufnehmen(karten)
Schreibe ich hingegen:
Code: Alles auswählen
def aufnehmen(self,karten):
self.__handkarten=self.__handkarten+karten
Ich schau mal ob ich ein minibeispiel hinbekomme...
Ging recht fix:
Code: Alles auswählen
class Spieler(object):
__handkarten=[]
def __init__(self):
pass
def aufnehmen(self,karten):
self.__handkarten+=karten
def zeige(self):
print self.__handkarten
class Tisch(object):
deck=['OE','OG','OH','OS','UE','UG','UH','US','AE','ZE','KE','9E','8E','7E','AG','ZG', 'KG','9G','8G','7G','AH','ZH','KH','9H','8H','7H','AS','ZS','KS','9S','8S','7S']
def __init__(self):
pass
def einladen(self):
self.mitspieler=[]
for i in range(0,4):
neu=Spieler()
self.mitspieler.append(neu)
def verteilen(self):
for i in range(0,4):
self.mitspieler[i].aufnehmen(self.deck[i*8-1:i*8+7])
if __name__=='__main__':
t=Tisch()
t.einladen()
t.verteilen()
for i in t.mitspieler:
i.zeige()
Deine Klasse 'Spieler()' verwendet ein Klassenattribut '__handkarten'. Klassenattribute sind im Gegensatz zu Instanzattributen für alle Exemplare (Instanzen) dieser Klasse gültig.
Wenn Du nun dieses Attribut per '__iadd__' veränderst, geschieht folgendes:
'karten' wird an das Spieler-Exemplar übergeben und dort in-place (daher das i bei iadd) dem Klassenattribut '__handkarten' (das für alle anderen Exemplare auch gültig ist) aufaddiert.
Wenn Du per '__add__' addierst, geschieht das:
'karten' wird an das Spieler-Exemplar übergeben. Durchwird eine (neue) Zuweisung an das dadurch (neu) angelegte Instanzattribut '__handkarten' eingeleitet. Diesem neuen Attribut wird dann durch der Additionswert vom Klassenattribut '__handkarten' und dem Übergabewert 'karten' zugewiesen.
Mit anderen Worten: Mit '__iadd__' veränderst Du das Klassenattribut, das für alle Exemplare gültig ist. Mit '__add__' legst Du ein neues Instanzattribut mit selben Namen an und weist diesem Attribut den Additionswert zu.
Folgendes verdeutlicht es vielleicht noch ein wenig (ich habe den doppelten Unterstrich entfernt, macht an dieser Stelle (und an kaum einer anderen
) keinen Sinn:
Gruß
mutetella
Wenn Du nun dieses Attribut per '__iadd__' veränderst, geschieht folgendes:
'karten' wird an das Spieler-Exemplar übergeben und dort in-place (daher das i bei iadd) dem Klassenattribut '__handkarten' (das für alle anderen Exemplare auch gültig ist) aufaddiert.
Wenn Du per '__add__' addierst, geschieht das:
'karten' wird an das Spieler-Exemplar übergeben. Durch
Code: Alles auswählen
self.__handkarten = ...Code: Alles auswählen
... self.__handkarten + kartenMit anderen Worten: Mit '__iadd__' veränderst Du das Klassenattribut, das für alle Exemplare gültig ist. Mit '__add__' legst Du ein neues Instanzattribut mit selben Namen an und weist diesem Attribut den Additionswert zu.
Folgendes verdeutlicht es vielleicht noch ein wenig (ich habe den doppelten Unterstrich entfernt, macht an dieser Stelle (und an kaum einer anderen
Code: Alles auswählen
class Spieler(object):
handkarten = []
def aufnehmen(self, karten):
self.handkarten = self.handkarten + karten
>>> s = Spieler()
>>> id(s.handkarten)
45167464
>>> s.aufnehmen([1, 2])
>>> id(s.handkarten)
45058528
class Spieler(object):
handkarten = []
def aufnehmen(self, karten):
self.handkarten += karten
>>> s = Spieler()
>>> id(s.handkarten)
45058240
>>> s.aufnehmen([1, 2])
>>> id(s.handkarten)
45058240mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)
-
Bobby-John-Joe
- User
- Beiträge: 7
- Registriert: Freitag 23. September 2011, 19:50
Alles klar, vielen Dank! Der Unterschied zwischen Klassen- und Instanzattributen war mir nicht bewusst. Das mit den Unterstrichen war nur ein versuch bei der Fehlersuche gewesen, die kommen jetzt wieder weg 
Gruß,
Bobby
Gruß,
Bobby
Noch ein paar Kleinigkeiten, die mir aufgefallen sind:
Das mag jetzt erstmal nach Korinthenkackerei ausschauen, aber mit gut lesbarem Code, der sich an gängige Konventionen hält, hast Du (und auch andere, die Deine Programme lesen möchten) mehr Freude...
Deine for-Schleifen sind so IMHO nicht ganz glücklich...
Gruß
mutetella
- Wie schon gesagt, doppelte Unterstriche würde ich vermeiden. Attribute oder Methoden mit einem Unterstrich markieren Namen, die nur innerhalb der Klasse/Methode verwendet werden und für den äußeren Code nicht von Bedeutung sind. 'handkarten' gehört nicht dazu.
- Konstanten wie 'deck' schreibt man in Großbuchstaben. Dadurch sehe ich im Code, dass es sich um einen festen Wert, wie z. B. Konfigurationsparameter, handelt.
- Bei einer Zuweisung gehören zwischen Name und Wert ein Leerschritt.
- Ebenso zwischen Rechenoperatoren, sofern sie bei der Übergabe an eine Funktion/Methode (also zwischen den Klammern) verwendet werden.
- Setze auch einen Leerschritt zwischen Listen-/Tuple-Elemente oder Parameter bei der Übergabe
- Wenn Du '__init__' nicht verwendest, könntest Du es auch gleich weglassen. Das ist aber jetzt meine persönliche Ansicht.
- Die Definition von Attributen würde ich immer in der '__init__' vornehmen, auch wenn eigentliche Werte erst woanderst zugewiesen werden. In der Regel geht man davon aus, dass verwendete Attribute dort erzeugt werden.
- Verwende zur Einrückung immer 4 Leerzeichen und hier im Forum das Code-Tag.
- Lass Zeilen nicht länger als 79 Zeichen sein.
Code: Alles auswählen
class Spieler(object):
def __init__(self):
self.handkarten = []
def aufnehmen(self, karten):
self.handkarten += karten
def zeige(self):
print self.handkarten
class Tisch(object):
DECK = ['OE', 'OG', 'OH', 'OS', 'UE', 'UG', 'UH', 'US', 'AE', 'ZE', 'KE',
'9E', '8E', '7E', 'AG', 'ZG', 'KG', '9G', '8G', '7G', 'AH', 'ZH',
'KH', '9H', '8H', '7H', 'AS', 'ZS', 'KS', '9S', '8S', '7S']
def __init__(self):
self.mitspieler = []
def einladen(self):
self.mitspieler = []
for i in range(0, 4):
self.mitspieler.append(Spieler())
def verteilen(self):
for i in range(0, 4):
self.mitspieler[i].aufnehmen(self.deck[i*8-1:i*8+7])
if __name__ == '__main__':
t = Tisch()
t.einladen()
t.verteilen()
for mitspieler in t.mitspieler:
mitspieler.zeige()Deine for-Schleifen sind so IMHO nicht ganz glücklich...
Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)
-
Bobby-John-Joe
- User
- Beiträge: 7
- Registriert: Freitag 23. September 2011, 19:50
darauf hab ich jetzt irgendwie auch gewartet
bin neulich mal über PEP8 gestolpert, da is mir aufgefallen, dass ich da einiges nicht so ganz sauber mache... Aber werde mich in Zukunft bemühen, aber speziell die vielen 'überflüssigen' Leerzeichen werden einige zeit brauchen bis sie sich eingeschliffen habe.
Andere Frage noch zwecks Code im Forum: wie bekomme ich Syntax highlighting? Sonst siehts immer so unübersichtlich aus...
Gruß,
Bobby
Andere Frage noch zwecks Code im Forum: wie bekomme ich Syntax highlighting? Sonst siehts immer so unübersichtlich aus...
Gruß,
Bobby
Die relevante Stelle aus der Doku ist der letzte Satz von
An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once.
@Bobby-John-Joe
Indem Du an den Anfang Deines Codes den Codetag python oder code=python (muss dann in eckigen Klammern stehen!) setzt und diesen am Ende mit /python oder /code (ebenfalls in eckigen Klammern!) abschließt.
Oder Du markierst den Codeabschnitt und klickst dann auf 'python', direkt über dem Texteingabefeld, unter dem Wort 'Schriftgröße'...
Gruß
mutetella
Indem Du an den Anfang Deines Codes den Codetag python oder code=python (muss dann in eckigen Klammern stehen!) setzt und diesen am Ende mit /python oder /code (ebenfalls in eckigen Klammern!) abschließt.
Oder Du markierst den Codeabschnitt und klickst dann auf 'python', direkt über dem Texteingabefeld, unter dem Wort 'Schriftgröße'...
Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)
