Forum: PC-Programmierung Python: Kurzes If-Else Statement?


von A. B. (sfalbuer)


Lesenswert?

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:

1
new += ('\n',' ') [i == len(message)-1]

Klappt aber nicht!

Any Idea?

Danke und Gruss!

: Bearbeitet durch User
von TestX (Gast)


Lesenswert?


von Spaminator (Gast)


Lesenswert?

new += '\n' if  i == len(message)-1 else ' '

von Tim (Gast)


Lesenswert?

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?

von A. B. (sfalbuer)


Lesenswert?

Hab mich hier versucht zu orientieren!

http://stackoverflow.com/questions/8500374/python-statement-of-short-if-else

m = (5, 100)[t == 0]

@Spanimator: Läuft. Danke

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

>
1
> new += (' ','\n') [i == len(message)-1]
2
>

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).
1
new += (" ok.", " FAILED!")[len(message)-1 == i]

von Sheeva P. (sheevaplug)


Lesenswert?

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:
1
if a == b:
2
  print a
3
else:
4
  print b

ist dasselbe wie jenes:
1
if a == b: print a
2
else: print b

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

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/

von Stefan S. (Gast)


Lesenswert?

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

von Alexander F. (alexf91)


Lesenswert?

Das sieht nach einem Anwendungsfall für str.join aus.
https://docs.python.org/2/library/stdtypes.html#str.join
1
new = ' '.join(message) + '\n'

Das sollte grundsätzlich das selbe machen wie die (vermutete) for 
Schleife, in der du dein if-Statement verwendest.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

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.

von A. B. (sfalbuer)


Lesenswert?

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

: Bearbeitet durch User
von Alexander F. (alexf91)


Lesenswert?

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:
1
new = ' '.join(map(str, message)) + '\n'

von Yalu X. (yalu) (Moderator)


Lesenswert?

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

: Bearbeitet durch Moderator
von A. B. (sfalbuer)


Lesenswert?

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.

: Bearbeitet durch User
von Alexander F. (alexf91)


Lesenswert?

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'"
4
5
s_for  = """\
6
new = ''
7
for i in xrange(len(messages)):
8
    new += str(messages[i])
9
    new += '\\n' if i == len(messages)-1 else ' '
10
"""
11
12
t_join = timeit.Timer(s_join, setup='messages = range(15)')
13
t_for = timeit.Timer(s_for, setup='messages = range(15)')
14
15
print 'Runtime join: %f' % t_join.timeit(1000000)
16
print 'Runtime for: %f' % t_for.timeit(1000000)

Jede Variante 10^6 Mal ausgeführt, wobei die Initialisierung von 
'messages' nicht gezählt wird.

Auf meiner Maschine ergibt das folgende Laufzeiten:
1
Runtime join: 2.654200
2
Runtime for: 6.141053

https://docs.python.org/2/library/timeit.html

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Sheeva P. (sheevaplug)


Lesenswert?

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'
4
100000 loops, best of 3: 2.17 µs per loop

[1] https://ipython.org/

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.