[Python-de] Syntax-Erweiterung für Schleifen in Python3

Sven R. Kunze srkunze at mail.de
Mi Apr 6 18:38:11 EDT 2016


Hi Volker,

On 06.04.2016 21:06, Dr. Volker Jaenisch wrote:
> Ansonsten ist dies wieder einer der Fälle in denen vergessen wird, dass
> man viele solcher Probleme
> durch das gesonderte Behandeln des ersten oder des letzten
> Schleifen-Elements einfach in den Griff bekommt.

Es geht darum, die Zukunft zu gestalten. Nicht, anderen zu sagen, dass 
ihre Umsetzungsvorschläge anders aussehen sollten, und eigentlich alles 
ganz einfach ist.

Das ist es nämlich nicht, weil eben gerade die Vielfalt an 
Lösungsalternativen, die nebenbei bemerkt nie 100% funktionieren, ein 
sehr gutes Indiz dafür sind, hier etwas bewegt werden sollte. Von daher 
finde ich es gut, wenn Thomas Vorschläge bringt. Die Länge des Threads 
und die Vorschläge auf python-ideas waren bemerkenswert und selbst auf 
python-de wurden gute Impulse gesetzt; DIE, wohl gemerkt, mehr für das 
Inkludieren in die Sprache sprechen als dagegen. Die Diskussion ist 
ergebnisoffen und sowohl ein Ablehnen als auch ein Zustimmen bedürfen 
sehr guter Argumente.

Ich hatte es auf python-ideas bereits irgendwo angesprochen: wir können 
im jetzigen Zustand nicht mehr so viel an der Sprache optimieren. Die 
meisten Verbesserungen sind eher Kleinigkeiten, bei denen die meisten 
Mailinglisten-Teilnehmer eher nicht richtig wissen, was sie damit 
anfangen sollen. Allerdings, gegen ein Feature, dass man selbst nie 
nutzt, sollte man eigentlich keine Argumente haben und man sollte eher 
versuchen zu verstehen, warum andere dieses Feature als sinnvoll erachten.

> try:
>      first_item = my_iterator.next()
>
>      connection = get_db_connection()
>      push_item_to_db(first_item, connection)
>
> except StopIteration:
>      ->> Exit oder continue oder break
>
> for item in my_iterator:
>      push_item_to_db(item, connection)
>
> Sagte, da gerade jemand, dass dies uneleganter Code ist, den man mit der
> neuen Syntax viel kompakter etc. hinschreiben könnte?
> Wenn es wirklich um Performance geht ist es nahezu unvermeidbar auch mal
> hässlichen Code zu schreiben - oder Cython oder C zu verwenden was
> letztlich
> nur bedeutet dem Schreiben von hässlichem Code einen neuen Namen zu
> verpassen. Das bedeutet aber im Umkehrschluss nicht, dass man den
> hässlichen Code,
> wie Du es Dir vorstellst mit Gewinn einfach im Interpreter verstecken
> kann. Denn ..
>
> .. Du schlägst mit
>>   - on-empty
>>   - pre-first (siehe oben)
>>   - on-break (wie bisher "else")
> drei zusätzliche Exits zum For-Konstrukt vor. IMHO zeichnet sich eine
> gute Programmiersprache unter anderem durch Ihre Orthogonalität und
> Einfachheit aus.

Es scheint mir so, als wäre Python dann nichts für dich. Frühe Versionen 
von Assembler wäre wohl deinen Anforderungen besser geeignet, da sie 
noch nicht so viel Komfort-Mnemonics erlaubten.

Orthogonalität ist, wie ich bei vielen Diskussionen in den 
Python-Mailinglisten (interessanter meist aus dem deutschsprachigem Raum 
stammend), ein abgedroschener Begriff. Aus meiner Sicht eine leere 
Worthülse um ein praxisfernes Konzept zu benamen.

Einfachheit und Komfort sind das Wichtigste und in genau diesem Bereich 
lässt Python bezüglich Schleifen ein wenig zu wünschen übrig. Da sind 
andere Sprachen schon weiter.

> Aus einfachen Konstrukten werden größere Konstrukte aggregiert.

Zum Glück ist das in Python nicht so. Python ist der Ausdruck des 
Komforts schlecht hin. Da wurde sich auch schon manchmal über die 
"Rechtwinkligkeit" hinweggesetzt, damit man es an manchen Stellen 
einfacher hat.

Ein kleines und zugegebenermaßen extremes Beispiel. Wenn man deiner 
Argumentation folgt, dann brauchen wir kein "if". Um dich damit zu zitieren:

"[Hier ist es auch] wieder einer der Fälle in denen vergessen wird, dass 
man viele solcher Probleme durch [die Verwendung einer besonderen 
while-Schleife] einfach in den Griff bekommt."


if condition:
     do

wird zu

while condition:
     do
     break


Toll, oder? Hätte Python kein "if" und nur ein "while" gehabt, hätten 
sicherlich einige damals genauso argumentiert wie du heute. Man kann das 
noch weiter ausführen und landet schließlich bei goto und schließlich 
bei Assembler mit JNE/JNZ.

In anderen Worten: korrekter Kontrollfluss, der alle Randfälle 100% 
beachtet, ist sehr sehr schwer umzusetzen für die meisten Entwickler. 
Kommen dann noch Konzepte wie Nebenläufigkeit und gemeinsamer Zustand 
hinzu, sind sehr viele verloren. Dich, Volker, betrifft das natürlich 
nicht, weil du, wie du weiter unten ausführst, Nebenläufigkeit und 
Kontrollfluss fehlerfrei handhabst.

Ist deine angepriesene Lösung thread-safe?
Warum ist der try-Block eigentlich so groß? -> Was passiert, wenn 
"get_db_connection", "push_item_to_db" eine StopIteration wirft? 
(unbeabsichtigt, wir machen alle Fehler, richtig?)

Das Rad neu erfinden. Vielleicht funktioniert diese Lösung in 93% der 
single-threaded Skripten und 35% der multi-threaded Skripten. Und die 
anderen? Naja, die erfinden das Rad nochmal ein bisschen anders neu.


Es ist schade, dass einige den möglichen Nutzen missachten bzw. aus 
ihrer Perspektive mit einem "brauch ich nicht, und überzeugt mich nicht" 
kontern, ohne wirklich auch die Perspektive des jeweils anderen einzunehmen.

:(


> Wenn man aber anfängt die Konstrukte auf der untersten Ebene komplizierter zu
> machen erzeugen
> diese zusätzlichen Freiheitsgrade Probleme bei der Optimierung. Ein JIT
> wie PyPy kann Python-Schleifen prima optimieren, eben weil sie einfach
> sind. Machen wir Python-Schleifen
> komplizierter, wird die Optimierung komplizierter und damit weniger
> effizient. Der Gewinn bei Deinen Corner-Cases wird durch die
> wegfallenden Optimierungsmöglichkeiten sofort wieder aufgefressen.

Falsch.

Automatisches Optimieren geht umso besser, um so mehr semantisch 
eindeutige Information vorhanden ist. Würde man den Kontrollfluss 
manuell, wie du ihn und andere vorgeschlagen haben, umsetzen, hätte der 
JIT-Compiler keinen blassen Schimmer, was ihm der Programmierer damit 
sagen wollte (Halte-Problem etc.). Wird diese aber in semantisch 
definierte Syntax gegossen, dann sind plötzlich völlig neue 
Möglichkeiten der Optimierung gegeben. Der Compiler *weiß* plötzlich, 
was der Quellcode *bedeuten soll* und kann entsprechend noch mehr 
optimieren.

Schade, dass du das Potenzial hier verkennst.

> Ein weiterer Denkanstoß: Bis heute werden numerische Bibliotheken in
> Fortran77 gepflegt und viel Code in den Wissenschaften wird sogar noch
> in F77 geschrieben obwohl Fortran uralt ist.
> Der Grund dafür ist unter anderem, dass Fortran77 so simpel ist. Daher
> kann Fortran mit optimierenden F2C-Compilern sehr stark optimiert und
> parallelisiert werden. Auch kann der Code durch automatisierte Verfahren
> auf seine Richtigkeit getestet werden. Die Einfachheit der Konstrukte
> von F77 verhinderte es komplizierten Code überhaupt hinzuschreiben.
> Daher war es möglich über Jahrzehnte hinweg den Code der numerischen
> Bibliotheken nahezu fehlerfrei zu bekommen.

Durch fehlende Sprachfeatures erfindet man das Rad ständig neu. Vielen 
können sich diesen Luxus nicht leisten, wodurch eine standardisierte und 
intuitive Lösung wünschenswert erscheint.

Darüber hinaus deuten Erfahrungen aus dem Compilerbau an, dass es 
einfacher ist einen solchen Compiler in modernen Hochsprachen, erstens 
viel einfacher zu schreiben ist und zweitens viel effizienter arbeitet. 
Warum? Weil man eben nicht das Rad jedes Mal neu erfindet, sondern sich 
auf das Wesentliche, das domänenspezifische Problem konzentrieren kann.

In der Größenordnung von Dekaden bekommt man jeden Code nahezu 
fehlerfrei und ich behaupte jetzt frei von der Leber weg, dass es 
weniger Dekaden mit Python gewesen wären. ;)

vG
Sven


Mehr Informationen über die Mailingliste python-de