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

Stefan Schwarzer sschwarzer at sschwarzer.net
So Apr 10 04:24:35 EDT 2016


On 2016-04-09 12:54, Christian Tanzer wrote:
> Stefan Schwarzer wrote at Sat, 9 Apr 2016 09:19:12 +0200:
>> Ich habe darüber nachgedacht, aber mir ist kein besserer
>> Ansatz eingefallen wie in den schon geschriebenen Antworten.
>>
>> Ein Kontextmanager fällt aus, da bei einem `with`-Statement
>> der Block nur einmal durchlaufen werden kann.
>>
>> Den Schleifenrumpf zu einer Funktion zu machen und in eine
>> Higher-Order-Function hineinzureichen wird deutlich
>> komplizierter als der schon beschriebene Ansatz mit Flag
>> oder Sentinel.
>>
>> Fällt euch noch etwas ein?
>
> Sicher. Generator-Funktionen sind das Mittel der Wahl, um sowas zu
> abstrahieren.

Christian, du hast natürlich recht, dass man bei Verwendung
einer Generator-Funktion keine Funktion für den
Schleifenrumpf übergeben muss.

> [Generator-Code-Beispiel]

Aber auch mit einer Generator-Funktion habe ich Zweifel,
dass sich mit einer derart abstrahierten API der diskutierte
Anwendungsfall vereinfacht.

Der Ausgangspunkt war:

  item = unset = object()
  for item in iterable:
      ...
  if item is unset:
      code_if_iterable_is_empty

Eine Generator-Funktion, die das abstrahiert, könnte dann so
aussehen:

  def iter_if_not_empty(iterable, if_empty_func):
      it = iter(iterable)
      try:
          first = next(it)
      except StopIteration:
          if_empty_func()
      else:
          yield first
          yield from it

und deren Anwendung so:

  def my_if_empty():
      code_if_iterable_is_empty

  for item in iter_if_not_empty(iterable, my_if_empty):
      ...

Wenn `my_if_empty` inline for der Schleife definiert wird,
muss man nicht zwingend Parameter übergeben, ansonsten kämen
noch `*if_empty_args` und `**if_empty_kwargs` dazu, die an
`iter_if_not_empty` übergeben und in `iter_if_not_empty`
beim Aufruf von `if_empty_func` übergeben werden müssten.

Wenn `my_if_empty` den lokalen Namespace modifizieren will,
in dem die Schleife (`for item in iter_if_not_empty(...)`)
steht, muss `my_if_empty` zusätzliche eine
`nonlocal`-Anwendung enthalten (die es zudem erst in
Python 3 gibt).

Alles in allem bin ich nicht überzeugt, dass es eine API
gibt, mit der sich die ursprüngliche Anwendung annähernd so
geradlinig implementieren lässt. Mit anderen Worten: die API
(`iter_if_not_empty` und Extra-Definition einer
`if_empty`-Funktion) verbessert aus meiner Sicht die Lage
nicht.

Was mich an dem Ansatz auch etwas stört, ist die
"Asymmetrie" zwischen der Schleife und dem "If empty"-Fall.
If finde, wie bei `if/else` sollten idealerweise beide in
der gleichen Form definiert werden. (Ich möchte nicht den
`if`-Fall inline schreiben und den `else`-Fall als Funktion
übergeben.) Vermutlich war das der Grund, warum ich die
Übergabe des Schleifenrumpfs als Funktion ins Spiel gebracht
hatte. Dann hätte man

  def my_loop_body():
      ...

  def my_if_empty():
      ...

  iter_if_not_empty(my_loop_body, my_if_empty)

Aber natürlich ist das _noch mehr_ Schreibaufwand.

Vielleicht habe ich noch etwas übersehen. Gibt es bessere
Ansätze?

Viele Grüße
Stefan


Mehr Informationen über die Mailingliste python-de