Hallo!
Ich arbeite mich gerade ein wenig in Python ein und möchte folgendes
Statement gerne in verkürzter Schreibweise schreiben:
1
if i == len(message)-1:
2
new += ('\n')
3
else:
4
new += ' '
Mich stört es dass ich für so eine Kleinigkeit gleich so viele Zeilen
verbrauche (Jaja ich weiß... Python funktioniert über Lesbarkeit..
trotzdem)
Habe schon etwas gesucht und dachte so müsste es gehen:
A. B. schrieb:> new += ('\n',' ') [i == len(message)-1]
So interessehalber für den Nicht-Python-Kenner:
Ich hätte in [...] eine Listenschreibweise vermutet.
Ist das so etwas wie nachgestelltes if/unless in Ruby?
Oder was ist der Gedankengang hinter diesem Konstrukt, unabhängig davon,
ob es nun im Detail richtig ist?
A. B. schrieb:> Habe schon etwas gesucht und dachte so müsste es gehen:>> new += ('\n',' ') [i == len(message)-1]>> Klappt aber nicht!
Du hast ja auch die beiden Tuple-Elemente vertauscht. So ist's richtig:
1
new += (' ','\n') [i == len(message)-1]
Oder etwas kürzer:
1
new += ' \n' [i == len(message)-1]
Ich würde aber trotzdem den If-Else-Ausdruck (wie von Spaminator
vorgeschlagen) bevorzugen, denn genau für solche Fälle ist dieses
Konstrukt gemacht.
Das '+=' und die komplizierte Bedingung lassen zudem ahnen, dass das
Ganze noch viel eleganter (pythonischer) formuliert werden kann, wenn
man noch etwas Programmkontext mit in die "Optimierung" einbezieht.
Ist klarer (selektiere ein Element aus dem Tuple)
Ausserdem kann hier ein Elemen auch mehr als 1 Zeichen sein, was im 2ten
beispiel nicht geht (weil 1 Zeichen aus String selektiert wird).
Yalu X. schrieb:> Ich würde aber trotzdem den If-Else-Ausdruck (wie von Spaminator> vorgeschlagen) bevorzugen, denn genau für solche Fälle ist dieses> Konstrukt gemacht.
Der eigentliche Grund für "dings if condition else bums" ist scheint mir
eher zu sein, daß es ungefähr tausendmal besser les- und wartbar ist.
;-)
Was der OP vorhat, ist allerdings... naja. Statt in Abhängigkeit von der
Länge des Strings entweder ein Leerzeichen oder ein Newline einzufügen,
könnte er seinen String komplett zusammenbauen und dann am Ende des
Strings einfach das letzte Zeichen überschreiben:
1
new[-1] = '\n'
Und wo wir gerade beim Nähkästchen sind: "string += anderer_string" wird
bei längeren Strings ziemlich teuer. Es ist eleganter und schneller, die
Daten erst in einer Liste zu sammeln und die dann erst am Ende mit
join() zusammenzufügen, also anstelle von
1
new = ''
2
for i in range(len(message)):
3
new += message[i]
4
if i == len(message)-1:
5
new += '\n'
6
else:
7
new += ' '
lieber so etwas zu benutzen:
1
new = list()
2
for line in message:
3
new.append(line + ' ')
4
new = ''.join(new)
5
new[-1] = '\n'
In Falle der Frage des OP ist aber vielleicht Folgendes noch eleganter:
1
new = ' '.join(message.readlines()) + '\n'
(Anstelle von message.readlines() kann man hier natürlich auch eine List
Comprehension oder die Builtin-Funktion map() benutzen, wenn man vor dem
Joinen noch eine Funktion auf die Zeilen anwenden will.)
Übrigens: wenn auf if, else, while, for ... nur eine Anweisung folgt,
ist ein Zeilenumbruch nicht nötig:
A. B. schrieb:> Mich stört es dass ich für so eine Kleinigkeit gleich so viele Zeilen> verbrauche
Warum?
> (Jaja ich weiß... Python funktioniert über Lesbarkeit.. trotzdem)
Da Code wesentlich öfter gelesen als geschrieben wird, tut man meistens
gut daran, ihn so lesbar wie nur möglich zu schreiben.
Durch "möglichst viel in eine Zeile packen" wird Code eher selten
besser.
Der Python Style Guide sagt denn auch:
"Compound statements (multiple statements on the same line) are
generally discouraged."
http://legacy.python.org/dev/peps/pep-0008/
>Durch "möglichst viel in eine Zeile packen" wird Code eher selten
besser.
Wenn man eine "einfache Operation" auf möglichst viele Zeilen verteilt
wird die Lesbarkeit und Verständlichkeit aber auch nicht wirklich
besser.
Stefan S. schrieb:> Wenn man eine "einfache Operation" auf möglichst viele Zeilen verteilt> wird die Lesbarkeit und Verständlichkeit aber auch nicht wirklich> besser.
Du kannst das gerne anders sehen. Ich halte mich im Zweifelsfall an das,
was der Erfinder der Sprache empfiehlt.
Mark B. schrieb:> Stefan S. schrieb:>> Wenn man eine "einfache Operation" auf möglichst viele Zeilen verteilt>> wird die Lesbarkeit und Verständlichkeit aber auch nicht wirklich>> besser.>> Du kannst das gerne anders sehen. Ich halte mich im Zweifelsfall an das,> was der Erfinder der Sprache empfiehlt.
In dem von dir verlinkten PEP geht es aber ausdrücklich um "Compound
Statements", also das Zusammenfassen mehrerer durch ";" getrennte
Anweisungen in einer Zeile. Im Beispiel rät er auch davon ab, bei einer
If-Anweisung den Teil nach dem Doppelpunkt in dieselbe Zeile zu
schreiben.
In diesem Thread geht es um eine If-Else-Anweisung, die in beiden
Zweigen den fast identischen Code enthält, nämlich
new += <Zeichenkonstante>
Der Vorschlag, diese If-Else-ANWEISUNG durch einen If-Else-AUSDRUCK¹ zu
ersetzen, widerpricht überhaupt nicht dem Style-Guide von Guido Rossum.
In meinen Augen ist es in diesem Fall sogar guter Stil, da dadurch das
"new +=" nur einmal auftaucht.
Man sieht daher sofort, dass hier an den String new in jedem Fall
etwas angehängt wird und eine Fallunterscheidung lediglich darin
erfolgt, was angehängt wird (in einem Fall ein '\n', im anderen ein
' ').
Dieses Idiom, komplexe Anweisungen durch Ausdrücke zu ersetzen, findet
sich in Python an vielen Stellen, bspw. bei der List-Comprehension, die
eine explizite For-Schleife (ggf. mit darin enthaltener If-Anweisung)
durch einen Ausdruck ersetzt wird, der an die in der Mathematik
gebräuchlichen bechreibende Darstellung von Mengen hat (wie bspw.
{(x,y)∈ℕ² | x²+y²=n}).
———————————
¹) Das ist etwas ganz anderes als eine in eine Zeile geschriebene
If-Anweisung.
Yalu X. schrieb:> In diesem Thread geht es um eine If-Else-Anweisung, die in beiden> Zweigen den fast identischen Code enthält, nämlich
SEHR schön, knackig prägnant geschrieben. Ganz GUT!
Kann nicht besser auf den Punkt gebracht werden. BRAVO!
...und nebenbei genau meine Meinung.
Sheeva P. schrieb:> Und wo wir gerade beim Nähkästchen sind: "string += anderer_string" wird> bei längeren Strings ziemlich teuer.
Hallo!
Vielen Dank schonmal für die Hilfe!
Um das noch mal zu erläutern: Ich muss ein String per UDP Senden der aus
15 Bytes besteht und diese jeweils mit einem Leerzeichen trennt. Am ende
dann noch die 10. Die einzelnen Daten liegen jeweils in Listen, es
handelt sich nicht um Strings. Daher muss ich auch erst den Datentyp
konvertieren:
1
for i in xrange(len(message)):
2
new += (str(message[i]))
3
new += '\n' if i == len(message)-1 else ' '
Funktioniert. Aber gegen Optimierung spricht natürlich nichts ;-)
Gruss
A.B
A. B. schrieb:> Die einzelnen Daten liegen jeweils in Listen, es> handelt sich nicht um Strings.
Auch in diesem Fall lässt sich das mit str.join lösen:
Alexander F. schrieb:> Auch in diesem Fall lässt sich das mit str.join lösen:> new = ' '.join(map(str, message)) + '\n'
Ja, so sieht das nach richtigem Python aus. Ich hätt's exakt genauso
gemacht :)
Jo, klappt. Danke.
@Yalu: Wieso ist das typisch Python? Weil es für jeden Kram Funktionen
gibt?Ich komme aus der Arduino-Ecke und da lässt sich das meiste über
solche Schleifen lösen.
In einer Schleife wird bei jeder Zuweisung zu 'new' ein neues Objekt
erzeugt.
Das liegt daran, dass String-Objekte nicht verändert werden können.
Mit 'join' hingegen werden nur zwei Objekte erzeugt: Eines beim Aufruf
von join und ein weiteres beim Anhängen von '\n'.
Hier ist ein Beispiel zum Messen der Laufzeiten:
1
import timeit
2
3
s_join = "new = ' '.join(map(str, messages)) + '\\n'"
A. B. schrieb:> Wieso ist das typisch Python?
In Python kann man viele Dinge eleganter formulieren als in vielen
klassischen Programmiersprachen wie bspw. C oder Pascal. Dazu gehört
auch die Vermeidung expliziter Schleifen, wofür Python einiges an
Hilfmitteln anbietet.
Die Programmierung ohne explizite Schleifen bringt mehrere Vorteile:
- Der Code wird kürzer.
- Es ist leichter erfassbar, was der Code tut, zumindest für den etwas
geübteren Python-Programmierer.
- Die Ausführung ist schneller, da weniger Bytecode-Instruktionen
ausgeführt werden müssen, so dass der Overhead des Interpreters nicht
so sehr ins Gewicht fällt
Im konkreten Beispiel ergibt sich ein weiterer Geschwindigkeitsvorteil
dadurch, dass nur ein einziges String-Objekt erzeugt wird. Da in Python
Strings immutable sind, wird in der Version mit der Schleife bei jeder
String-Operation (davon gibt bes zwei pro Schleifendurchlauf) ein neues
String-Objekt angelegt. Von diesen Strings wird schließlich nur das
letzte auch tatsächlich weitergenutzt. Alle anderen werden irgendwann
durch die Garbage-Collection weggeräumt, was aber erneut Rechenzeit
kostet.
Generell gilt Python wie die meisten Skriptsprachen als sehr langsam in
der Ausführung. Wenn man es aber schafft, einen Großteil der Arbeit
durch Funktionen der Standardbibliothek ausführen zu lassen (von denen
die zeitkritischen meist in C geschrieben sind) kann auch ein Python-
Program ziemlich schnell sein. Zudem muss man dadurch das Rad nicht
immer wieder neu erfinden.
Alexander F. schrieb:> In einer Schleife wird bei jeder Zuweisung zu 'new' ein neues Objekt> erzeugt.> Das liegt daran, dass String-Objekte nicht verändert werden können.> Mit 'join' hingegen werden nur zwei Objekte erzeugt: Eines beim Aufruf> von join und ein weiteres beim Anhängen von '\n'.>> Hier ist ein Beispiel zum Messen der Laufzeiten:> [...]
Wärmstens empfohlen: iPython [1]. Interaktiver Modus auf Steroiden!
1
In [1]: message = range(15)
2
3
In [2]: %timeit ' '.join(map(str, message)) + '\n'