Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 531 – Existenzprüfungsoperatoren

Autor:
Alyssa Coghlan <ncoghlan at gmail.com>
Status:
Zurückgezogen
Typ:
Standards Track
Erstellt:
25-Okt-2016
Python-Version:
3.7
Post-History:
28-Okt-2016

Inhaltsverzeichnis

Zusammenfassung

Inspiriert von PEP 505 und den damit verbundenen Diskussionen schlägt diese PEP die Hinzufügung von zwei neuen Kontrollflussoperatoren zu Python vor

  • Existenzprüfungs-Vorbedingung („exists-then“): expr1 ?then expr2
  • Existenzprüfungs-Fallback („exists-else“): expr1 ?else expr2

sowie die folgenden Abkürzungen für gängige Existenzprüfungs-Ausdrücke und -Anweisungen

  • Existenzprüfungs-Attributzugriff: obj?.attr (für obj ?then obj.attr)
  • Existenzprüfungs-Subskription: obj?[expr] (für obj ?then obj[expr])
  • Existenzprüfungs-Zuweisung: value ?= expr (für value = value ?else expr)

Das übliche ? Symbol in diesen neuen Operator-Definitionen zeigt an, dass sie ein neues „Existenzprüfungs“-Protokoll verwenden und nicht das etablierte Wahrheitsprüfungs-Protokoll, das von if-Anweisungen, while-Schleifen, Comprehensions, Generator-Ausdrücken, bedingten Ausdrücken, logischer Konjunktion und logischer Disjunktion verwendet wird.

Dieses neue Protokoll wäre als operator.exists verfügbar, mit den folgenden Merkmalen

  • Typen können eine neue magische Methode __exists__ (Python) oder einen Slot tp_exists (C) definieren, um das Standardverhalten zu überschreiben. Diese optionale Methode hat die gleiche Signatur und mögliche Rückgabewerte wie __bool__.
  • operator.exists(None) gibt False zurück
  • operator.exists(NotImplemented) gibt False zurück
  • operator.exists(Ellipsis) gibt False zurück
  • float, complex und decimal.Decimal überschreiben die Existenzprüfung so, dass NaN Werte False zurückgeben und andere Werte (einschließlich Nullwerte) True zurückgeben
  • Für jeden anderen Typ gibt operator.exists(obj) standardmäßig True zurück. Am wichtigsten ist, dass Werte, die in einem Wahrheitsprüfungskontext zu False ausgewertet werden (Nullen, leere Container), in einem Existenzprüfungskontext immer noch zu True ausgewertet werden

Rücknahme eines PEP

Beim Posten dieser PEP zur Diskussion auf python-ideas [4] bat ich die Gutachter, 3 übergreifende Designfragen zu berücksichtigen, bevor wir uns mit den Einzelheiten dieses spezifischen syntaktischen Vorschlags befassen

1. Sind wir uns kollektiv einig, dass „Existenzprüfung“ ein nützliches allgemeines Konzept in der Softwareentwicklung ist und sich vom Konzept der „Wahrheitsprüfung“ unterscheidet? 2. Sind wir uns kollektiv einig, dass das Python-Ökosystem von einem Existenzprüfungs-Protokoll profitieren würde, das eine Verallgemeinerung von Algorithmen (insbesondere Kurzschlussalgorithmen) über verschiedene „Datenfehlende“-Indikatoren hinweg ermöglicht, einschließlich derer, die in der Sprachdefinition, der Standardbibliothek und benutzerdefiniertem Code definiert sind? 3. Sind wir uns kollektiv einig, dass es einfacher wäre, ein solches Protokoll effektiv zu nutzen, wenn Existenzprüfungs-Äquivalente zu den Wahrheitsprüfungs-Kontrollflussoperatoren „and“ und „or“ verfügbar wären?

Während die Antworten auf die erste Frage im Allgemeinen positiv ausfielen, wurde schnell klar, dass die Antwort auf die zweite Frage „Nein“ lautet.

Steven D’Aprano formulierte das Gegenargument gut in [5], aber die allgemeine Idee ist, dass wir bei der Prüfung auf „fehlende Daten“-Sentinels fast immer nach einem *spezifischen* Sentinel-Wert suchen und nicht nach *irgendeinem* Sentinel-Wert.

NotImplemented existiert beispielsweise, weil None ein potenziell legitimes Ergebnis überladener arithmetischer Operatoren sein kann und die Ausnahmebehandlung zu viel Laufzeit-Overhead verursacht, um für die Operandenzuordnung nützlich zu sein.

Ähnlich existiert Ellipsis zur Unterstützung der mehrdimensionalen Indizierung, da None in einem Indizierungskontext bereits eine andere Bedeutung hat (was die Verwendung der Standard-Start- oder Stoppindizes oder der Standard-Schrittgröße angibt).

In der Mathematik besteht der Wert von NaN darin, dass es sich *programmatisch* wie ein normaler Wert seines Typs verhält (z. B. alle üblichen Attribute und Methoden aufweist), während es sich *arithmetisch* gemäß den mathematischen Regeln für die Handhabung von NaN -Werten verhält.

Da dieses Kern-Designkonzept ungültig ist, ergibt der Vorschlag als Ganzes keinen Sinn und wird dementsprechend zurückgezogen.

Die Diskussion des Vorschlags löste jedoch die Überlegung eines potenziellen protokollbasierten Ansatzes aus, um die vorhandenen Operatoren and, or und if-else flexibler zu gestalten [6], ohne neue Syntax einzuführen. Ich werde dies als weitere mögliche Alternative zu PEP 505 aufschreiben.

Beziehung zu anderen PEPs

Während diese PEP von Mark Haases exzellenter Arbeit bei der Zusammenstellung von PEP 505 inspiriert wurde und darauf aufbaut, konkurriert sie letztendlich mit dieser PEP aufgrund erheblicher Unterschiede in den Details des vorgeschlagenen Syntax und Semantik für die Funktion.

Sie präsentiert auch eine andere Perspektive auf die Begründung der Änderung, indem sie sich auf die Vorteile für bestehende Python-Benutzer konzentriert, da sich die typischen Anforderungen der Anwendungs- und Serviceentwicklung tatsächlich ändern. Es ist kein Zufall, dass ähnliche Funktionen nun in mehreren Programmiersprachen erscheinen, und obwohl es eine gute Idee ist, von der Art und Weise zu lernen, wie andere Sprachdesigner das Problem angehen, sind die anderswo gesetzten Präzedenzfälle relevanter für *wie* wir dieses Problem angehen würden, als dafür, ob wir denken, dass es ein Problem ist, das wir überhaupt angehen sollten.

Begründung

Existenzprüfungs-Ausdrücke

Eine immer häufigere Anforderung in der modernen Softwareentwicklung ist die Notwendigkeit, mit „semi-strukturierten Daten“ zu arbeiten: Daten, deren Struktur im Voraus bekannt ist, aber Teile davon können zur Laufzeit fehlen, und die Software, die diese Daten verarbeitet, wird erwartet, dass sie sich anmutig verschlechtert (z. B. durch Weglassen von Ergebnissen, die von den fehlenden Daten abhängen), anstatt sofort zu versagen.

Einige besonders häufige Fälle, in denen dieses Problem auftritt, sind

  • die Handhabung optionaler Anwendungskonfigurationseinstellungen und Funktionsparameter
  • die Handhabung von Ausfällen externer Dienste in verteilten Systemen
  • die Handhabung von Datensätzen, die einige teilweise Datensätze enthalten

Die beiden letzteren Fälle sind die primäre Motivation für diese PEP – während die Notwendigkeit, mit optionalen Konfigurationseinstellungen und Parametern umzugehen, eine Designanforderung ist, die mindestens so alt ist wie Python selbst, sind der Aufstieg von Public Cloud-Infrastrukturen, die Entwicklung von Softwaresystemen als kollaborative Netzwerke verteilter Dienste und die Verfügbarkeit großer öffentlicher und privater Datensätze zur Analyse bedeuten, dass die Fähigkeit, Operationen angesichts partieller Serviceausfälle oder partieller Datenverfügbarkeit anmutig zu degradieren, zu einer wesentlichen Funktion moderner Programmierumgebungen wird.

Derzeit kann das Schreiben solcher Software in Python in der Tat umständlich sein, da Ihr Code mit Ausdrücken übersät ist wie

  • value1 = expr1.field.of.interest if expr1 is not None else None
  • value2 = expr2["field"]["of"]["interest"] if expr2 is not None else None
  • value3 = expr3 if expr3 is not None else expr4 if expr4 is not None else expr5

Wenn diese nur gelegentlich auftreten, kann die Erweiterung auf vollständige Anweisungsformen helfen, die Lesbarkeit zu verbessern, aber wenn Sie 4 oder 5 davon hintereinander haben (was in Daten transformations Pipelines eine ziemlich häufige Situation ist), dann hilft die Ersetzung durch 16 oder 20 Zeilen bedingter Logik wirklich nicht weiter.

Die Erweiterung der drei obigen Beispiele auf diese Weise hilft hoffentlich zu veranschaulichen, dass

if expr1 is not None:
    value1 = expr1.field.of.interest
else:
    value1 = None
if expr2 is not None:
    value2 = expr2["field"]["of"]["interest"]
else:
    value2 = None
if expr3 is not None:
    value3 = expr3
else:
    if expr4 is not None:
        value3 = expr4
    else:
        value3 = expr5

Die kombinierte Wirkung der Vorschläge in dieser PEP besteht darin, dass die obigen Beispielausdrücke stattdessen geschrieben werden können als

  • value1 = expr1?.field.of.interest
  • value2 = expr2?["field"]["of"]["interest"]
  • value3 = expr3 ?else expr4 ?else expr5

In diesen Formen sind fast alle dem Leser präsentierten Informationen sofort relevant für die Frage „Was macht dieser Code?“, während der Boilerplate-Code zur Handhabung fehlender Daten, indem er sie an die Ausgabe weiterleitet oder zu einer Alternative zurückfällt, auf zwei Verwendungen des ? -Symbols und zwei Verwendungen des ?else -Schlüsselworts reduziert wurde.

In den ersten beiden Beispielen wurde die 31 Zeichen lange Boilerplate-Klausel if exprN is not None else None (minimal 27 Zeichen für einen einzelnen Buchstaben-Variablennamen) durch ein einzelnes ? -Zeichen ersetzt, was das Signal-zu-Rausch-Verhältnis der Zeilen erheblich verbessert (insbesondere wenn es die Verwendung von aussagekräftigeren Variablen- und Feldnamen fördert, anstatt sie nur zur Kürze des Ausdrucks zu verkürzen).

Im letzten Beispiel werden zwei Instanzen der 21 Zeichen langen Boilerplate if exprN is not None (minimal 17 Zeichen) durch einzelne Zeichen ersetzt, was erneut das Signal-zu-Rausch-Verhältnis erheblich verbessert.

Darüber hinaus ist jede unserer 5 „Unterausdrücke von potenziellem Interesse“ genau einmal enthalten, anstatt dass 4 davon dupliziert oder in eine benannte Variable extrahiert werden müssen, um zuerst zu prüfen, ob sie existieren.

Der Existenzprüfungs-Vorbedingungsoperator ist hauptsächlich dazu gedacht, eine klare konzeptionelle Grundlage für die Existenzprüfungs-Attributzugriffs- und Subskriptionsoperatoren zu bieten

  • obj?.attr ist ungefähr äquivalent zu obj ?then obj.attr
  • obj?[expr] ist ungefähr äquivalent zu obj ?then obj[expr]

Der Hauptunterschied in der Semantik zwischen den Kurzformen und ihren erweiterten Äquivalenten besteht darin, dass der gemeinsame Unterausdruck links vom Existenzprüfungsoperator in der Kurzform nur einmal ausgewertet wird (ähnlich dem Vorteil von erweiterten Zuweisungsanweisungen).

Existenzprüfungs-Zuweisung

Existenzprüfungs-Zuweisung wird als relativ einfache Erweiterung der Konzepte dieser PEP vorgeschlagen, um auch das gängige Konfigurationshandhabungsidiom abzudecken

  • value = value if value is not None else expensive_default()

indem es stattdessen abgekürzt werden kann als

  • value ?= expensive_default()

Dies ist hauptsächlich dann vorteilhaft, wenn das Ziel eine Subskriptionsoperation oder ein Unterattribut ist, da die PEP selbst ohne diese spezielle Änderung immer noch erlauben würde, dieses Idiom zu aktualisieren zu

  • value = value ?else expensive_default()

Das Hauptargument *gegen* die Hinzufügung dieser Form ist, dass sie zugegebenermaßen mehrdeutig ist und entweder bedeuten könnte

  • value = value ?else expensive_default(); oder
  • value = value ?then value.subfield.of.interest

Die zweite Form ist überhaupt nicht nützlich, aber wenn diese Bedenken als signifikant genug erachtet würden, um angegangen zu werden, während die erweiterte Zuweisungsfunktion beibehalten wird, könnte das vollständige Schlüsselwort in die Syntax aufgenommen werden

  • value ?else= expensive_default()

Alternativ könnte die erweiterte Zuweisung ganz aus dem aktuellen Vorschlag gestrichen und möglicherweise zu einem späteren Zeitpunkt erneut geprüft werden.

Existenzprüfungs-Protokoll

Das Existenzprüfungs-Protokoll wird hauptsächlich in diesen Vorschlag aufgenommen, um Proxy-Objekte (z. B. lokale Darstellungen von Remote-Ressourcen) und Mock-Objekte, die beim Testen verwendet werden, zu ermöglichen, die Nichtexistenz von Zielressourcen korrekt anzuzeigen, auch wenn das Proxy- oder Mock-Objekt selbst nicht None ist.

Mit diesem Protokoll scheint es jedoch natürlich, es zu erweitern, um eine typunabhängige Möglichkeit zur Prüfung auf NaN -Werte in numerischen Typen zu bieten – derzeit muss man sich des genauen Datentyps bewusst sein, mit dem man arbeitet (z. B. eingebaute Floats, eingebaute komplexe Zahlen, das decimal-Modul) und die entsprechende Operation verwenden (z. B. math.isnan, cmath.isnan, decimal.getcontext().is_nan(), bzw.)

Ebenso scheint es vernünftig zu erklären, dass die anderen eingebauten Platzhalter-Singletons, Ellipsis und NotImplemented, ebenfalls als Objekte gelten, die die Abwesenheit von Daten eher repräsentieren als Daten selbst.

Vorgeschlagene symbolische Notation

Python hatte historisch gesehen nur eine Art von implizitem booleschem Kontext: Wahrheitsprüfung, die direkt über die eingebaute Funktion bool() aufgerufen werden kann. Da diese PEP eine neue Art von Kontrollflussoperation auf Basis der Existenzprüfung und nicht der Wahrheitsprüfung vorschlägt, wird es als wertvoll erachtet, im Code direkt eine Erinnerung daran zu haben, wann eine Existenzprüfung anstelle einer Wahrheitsprüfung verwendet wird.

Das mathematische Symbol für Existenzaussagen ist U+2203 „ES EXISTIERT“:

Dementsprechend wäre ein möglicher Ansatz für die in dieser PEP vorgeschlagenen syntaktischen Ergänzungen die Verwendung dieser bereits definierten mathematischen Notation

  • expr1 ∃then expr2
  • expr1 ∃else expr2
  • obj∃.attr
  • obj∃[expr]
  • target ∃= expr

Es gibt jedoch zwei Hauptprobleme mit diesem Ansatz, eines praktisch und eines pädagogisch.

Das praktische Problem ist das übliche: Die meisten Tastaturen bieten keine einfache Möglichkeit, mathematische Symbole einzugeben, außer denen, die in der Grundarithmetik verwendet werden (selbst die in dieser PEP erscheinenden Symbole wurden letztendlich kopiert und eingefügt [3], anstatt direkt eingegeben zu werden).

Das pädagogische Problem ist, dass die Symbole für Existenzaussagen () und universelle Aussagen () für die meisten Menschen nicht so vertraut sein werden wie grundlegende arithmetische Operatoren, sodass wir die vorgeschlagene Syntax durch die Übernahme von nicht wirklich verständlicher machen würden.

Im Gegensatz dazu ist ? eines der wenigen verbleibenden ungenutzten ASCII-Satzzeichen in der Python-Syntax, was es als Kandidaten für ein syntaktisches Kennzeichen für „Diese Kontrollflussoperation basiert auf einer Existenzprüfung, nicht auf einer Wahrheitsprüfung“ verfügbar macht.

Dieser Weg hätte auch den Vorteil, dass die Python-Syntax mit der entsprechenden Syntax in anderen Sprachen übereinstimmt, die ähnliche Funktionen anbieten.

Basierend auf der vorhandenen Zusammenfassung in PEP 505 und den Wikipedia-Artikeln zum „sicheren Navigationsoperator [1] und zum „Null-Coalescing-Operator“ [2] sehen wir

  • Die ?. -Syntax für den Existenzprüfungs-Attributzugriff stimmt genau mit
    • dem „sicheren Navigations“-Attributzugriffsoperator in C# (?.)
    • dem „optionalen Verkettungs“-Operator in Swift (?.)
    • dem „sicheren Navigations“-Attributzugriffsoperator in Groovy (?.)
    • dem „bedingten Mitgliedzugriffs“-Operator in Dart (?.)
  • Die ?[] -Syntax für den Existenzprüfungs-Attributzugriff stimmt genau mit
    • dem „sicheren Navigations“-Subskriptionsoperator in C# (?[])
    • dem „optionalen Subskriptions“-Operator in Swift (?[].)
  • Die ?else -Syntax für den Existenzprüfungs-Fallback stimmt semantisch mit
    • dem „Null-Coalescing“-Operator in C# (??)
    • dem „Null-Coalescing“-Operator in PHP (??)
    • dem „Nil-Coalescing“-Operator in Swift (??)

Um klar zu sein, das sind nicht die einzigen Schreibweisen dieser Operatoren in anderen Sprachen, aber sie sind die gängigsten, und das ? -Symbol ist bei weitem das gängigste syntaktische Kennzeichen (vermutlich angeregt durch die Verwendung von ? zur Einleitung des „then“-Zweigs in C-ähnlichen bedingten Ausdrücken, die viele dieser Sprachen ebenfalls anbieten).

Vorgeschlagene Schlüsselwörter

Angesichts des symbolischen Kennzeichens ? wäre es syntaktisch eindeutig, die Existenzprüfungs-Vorbedingungs- und Fallback-Operationen mit denselben Schlüsselwörtern wie ihre Wahrheitsprüfungs-Gegenstücke zu schreiben

  • expr1 ?and expr2 (anstelle von expr1 ?then expr2)
  • expr1 ?or expr2 (anstelle von expr1 ?else expr2)

Dies ist jedoch, obwohl syntaktisch eindeutig geschrieben, unglaublich schwer auszusprechen (Wie spricht man „?“ aus?) und auch schwer zu beschreiben (Angesichts wiederverwendeter Schlüsselwörter gibt es keine offensichtlichen Kurzbezeichnungen für „Existenzprüfungs-Vorbedingung (?and)“ und „Existenzprüfungs-Fallback (?or)“, die sie von „logischer Konjunktion (and)“ und „logischer Disjunktion (or)“ unterscheiden würden).

Wir könnten versuchen, die Leute zu ermutigen, das ? -Symbol als „exists“ auszusprechen, wodurch die Kurzbezeichnungen zum „exists-and-Ausdruck“ und zum „exists-or-Ausdruck“ würden, aber es gäbe keine Möglichkeit, diese Namen rein durch das Lesen im Code zu erraten.

Stattdessen nutzt diese PEP die vorgeschlagene symbolische Syntax, um ein neues Schlüsselwort (?then) einzuführen und ein bestehendes zu leihen (?else), so dass Leute eindeutig „then-Ausdrücke“ und „else-Ausdrücke“ bezeichnen können.

Diese Schlüsselwörter passen auch gut zu den bedingten Ausdrücken, die semantisch den vorgeschlagenen Ausdrücken entsprechen.

Für ?else -Ausdrücke ist expr1 ?else expr2 äquivalent zu

_lhs_result = expr1
_lhs_result if operator.exists(_lhs_result) else expr2

Hier ist die Parallele klar, da das else expr2 am Ende sowohl der abgekürzten als auch der erweiterten Formen steht.

Für ?then -Ausdrücke ist expr1 ?then expr2 äquivalent zu

_lhs_result = expr1
expr2 if operator.exists(_lhs_result) else _lhs_result

Hier ist die Parallele nicht so offensichtlich aufgrund von Pythons traditionell anonymen „then“-Klauseln (eingeleitet durch : in if -Anweisungen und angehängt durch if in bedingten Ausdrücken), aber sie ist immer noch relativ klar, solange man bereits mit der „if-then-else“-Erklärung des bedingten Kontrollflusses vertraut ist.

Risiken und Bedenken

Lesbarkeit

Das Erlernen der neuen Syntax erfordert hauptsächlich die Verinnerlichung von zwei Konzepten

  • Ausdrücke, die ? enthalten, beinhalten eine Existenzprüfung und können Kurzschlüsse machen
  • Wenn None oder ein anderer „nicht existierender“ Wert eine erwartete Eingabe ist und die korrekte Handhabung darin besteht, diesen an das Ergebnis weiterzugeben, dann sind die Existenzprüfungsoperatoren wahrscheinlich das, was Sie wollen

Derzeit sind diese Konzepte nicht explizit auf Sprachebene dargestellt, daher geht es darum, die verschiedenen idiomatischen Muster zu erkennen und zu verwenden, die auf bedingten Ausdrücken und Anweisungen basieren.

Magische Syntax

Es gibt nichts an ? als syntaktisches Element, das inhärent is not None oder operator.exists impliziert. Die derzeitige Hauptverwendung von ? als Symbol in Python-Code ist als nachgestelltes Suffix in IPython-Umgebungen, um Hilfeinformationen für das Ergebnis des vorherigen Ausdrucks anzufordern.

Die Idee der Existenzprüfung profitiert jedoch wirklich von einem durchgängigen visuellen Marker, der sie von der Wahrheitsprüfung unterscheidet, und das erfordert eine ein-Zeichen-symbolische Syntax, wenn wir es überhaupt tun wollen.

Konzeptionelle Komplexität

Dieser Vorschlag hebt das derzeit ad-hoc und informelle Konzept der „Existenzprüfung“ auf den Status einer syntaktischen Sprachfunktion mit einem klar definierten Operatorprotokoll an.

In vielerlei Hinsicht sollte dies die konzeptionelle Komplexität der Sprache insgesamt *reduzieren*, da viele Erwartungen besser zwischen der Wahrheitsprüfung mit bool(expr) und der Existenzprüfung mit operator.exists(expr) übereinstimmen werden, als sie derzeit zwischen der Wahrheitsprüfung und der Existenzprüfung mit expr is not None (oder expr is not NotImplemented im Kontext der Operandenzuordnung oder den verschiedenen NaN-Prüfoperationen in mathematischen Bibliotheken) übereinstimmen.

Als einfaches Beispiel für die neuen Parallelen, die durch diese PEP eingeführt werden, vergleichen Sie

all_are_true = all(map(bool, iterable))
at_least_one_is_true = any(map(bool, iterable))
all_exist = all(map(operator.exists, iterable))
at_least_one_exists = any(map(operator.exists, iterable))

Design-Diskussion

Nuancen bei der Verknüpfung von Existenzprüfungs-Ausdrücken

Ähnliche Nuancen treten beim Verketten von Existenzprüfungs-Ausdrücken auf, wie sie bereits beim Verketten von logischen Operatoren auftreten: Das Verhalten kann überraschend sein, wenn die rechte Seite eines der Ausdrücke in der Kette selbst einen Wert zurückgibt, der nicht existiert.

Infolgedessen wäre value = arg1 ?then f(arg1) ?else default() aus im Wesentlichen demselben Grund fragwürdig wie value = cond and expr1 or expr2 fragwürdig ist: ersteres wird default() auswerten, wenn f(arg1) None zurückgibt, genauso wie letzteres expr2 auswerten wird, wenn expr1 in einem booleschen Kontext zu False ausgewertet wird.

Mehrdeutige Interaktion mit bedingten Ausdrücken

In dem derzeitigen Vorschlag ist Folgendes ein Syntaxfehler

  • value = f(arg) if arg ?else default

Während das Folgende eine gültige Operation ist, die eine zweite Bedingung prüft, wenn die erste nicht existiert, anstatt nur falsch zu sein

  • value = expr1 if cond1 ?else cond2 else expr2

Das oben beschriebene Verkettungsproblem bedeutet, dass argumentiert werden kann, dass die erste Operation stattdessen äquivalent sein sollte zu

  • value = f(arg) if operator.exists(arg) else default

wodurch die zweite in der wohl klareren Form geschrieben werden muss

  • value = expr1 if (cond1 ?else cond2) else expr2

Alternativ könnte die erste Form ein Syntaxfehler bleiben, und das Existenzprüfungs-Symbol könnte stattdessen an das Schlüsselwort if angehängt werden

  • value = expr1 if? cond else expr2

Existenzprüfung in anderen Wahrheitsprüfungskontexten

Das Wahrheitsprüfungs-Protokoll wird derzeit in den folgenden syntaktischen Konstrukten verwendet

  • logische Konjunktion (and-Ausdrücke)
  • logische Disjunktion (or-Ausdrücke)
  • bedingte Ausdrücke (if-else-Ausdrücke)
  • if-Anweisungen
  • while-Schleifen
  • Filter-Klauseln in Comprehensions und Generator-Ausdrücken

In der aktuellen PEP ist der Wechsel von der Wahrheitsprüfung mit and und or zur Existenzprüfung eine Frage der Ersetzung durch die neuen Schlüsselwörter ?then und ?else an den entsprechenden Stellen.

Für andere Wahrheitsprüfungskontexte schlägt sie entweder den Import und die Verwendung der operator.exists API vor oder die Beibehaltung des aktuellen Idioms der Prüfung speziell auf expr is not None (oder das kontextabhängige Äquivalent).

Die einfachste Verbesserung in dieser Hinsicht wäre die Aufwertung der vorgeschlagenen exists() API von einer Operator-Modulfunktion zu einer neuen eingebauten Funktion.

Alternativ könnte das ? -Existenzprüfungs-Symbol als Modifikator für die Schlüsselwörter if und while unterstützt werden, um die Verwendung einer Existenzprüfung anstelle einer Wahrheitsprüfung anzuzeigen.

Es ist jedoch keineswegs klar, ob die potenziellen Konsistenzvorteile, die sich aus einer der beiden Vorschläge ergeben, die zusätzliche Störung rechtfertigen würden, daher wurden sie derzeit vom Vorschlag weggelassen.

Definition erwarteter invarianter Beziehungen zwischen __bool__ und __exists__

Die PEP lässt die Definition von __bool__ auf allen bestehenden Typen unverändert, was sicherstellt, dass der gesamte Vorschlag abwärtskompatibel bleibt, aber zu folgenden Fällen führt, in denen bool(obj) True zurückgibt, aber das vorgeschlagene operator.exists(obj) False zurückgeben würde

  • NaN -Werte für float, complex und decimal.Decimal
  • Ellipsis
  • NotImplemented

Das Hauptargument für eine mögliche Änderung dieser ist, dass es einfacher wird, das potenzielle Verhalten von Code zu verstehen, wenn wir eine empfohlene Invariante haben, die besagt, dass Werte, die anzeigen, dass sie in einem Existenzprüfungskontext nicht existieren, sich auch als False in einem Wahrheitsprüfungskontext melden sollten.

Das Versäumnis, eine solche Invariante zu definieren, würde zu arguably seltsamen Ergebnissen führen, wie z. B. dass float("NaN") ?else 0.0 0.0 zurückgibt, während float("NaN") or 0.0 NaN zurückgibt.

Einschränkungen

Beliebige Sentinel-Objekte

Dieser Vorschlag versucht nicht, syntaktische Unterstützung für das „Sentinel-Objekt“-Idiom bereitzustellen, bei dem None ein zulässiger expliziter Wert ist, so dass ein separates Sentinel-Objekt zur Anzeige fehlender Werte definiert wird

_SENTINEL = object()
def f(obj=_SENTINEL):
    return obj if obj is not _SENTINEL else default_value()

Dies könnte potenziell auf Kosten einer erheblich komplexeren Definition und Verwendung des Existenzprotokolls unterstützt werden

  • Auf der Python-Ebene würden operator.exists und __exists__ Implementierungen das leere Tupel zurückgeben, um Nichtexistenz anzuzeigen, und andernfalls ein Singleton-Tupel mit einem Verweis auf das Objekt zurückgeben, das als Ergebnis der Existenzprüfung verwendet werden soll
  • Auf der C-Ebene würden tp_exists Implementierungen NULL zurückgeben, um Nichtexistenz anzuzeigen, und andernfalls einen PyObject * -Zeiger als Ergebnis der Existenzprüfung zurückgeben

Angesichts dieser Änderung könnte das Sentinel-Objekt-Idiom wie folgt umgeschrieben werden

class Maybe:
  SENTINEL = object()
  def __init__(self, value):
      self._result = (value,) is value is not self.SENTINEL else ()
  def __exists__(self):
      return self._result

def f(obj=Maybe.SENTINEL):
    return Maybe(obj) ?else default_value()

Ich glaube jedoch nicht, dass Fälle, in denen die 3 vorgeschlagenen Standard-Sentinel-Werte (d. h. None, Ellipsis und NotImplemented) nicht verwendet werden können, annähernd häufig genug sind, um die zusätzliche Protokollkomplexität und den Verlust der Symmetrie zwischen __bool__ und __exists__ zu rechtfertigen.

Spezifikation

Der Abstract gibt bereits den Tenor des Vorschlags wieder und die Begründung liefert einige spezifische Beispiele. Wenn genügend Interesse an der Grundidee besteht, dann muss eine vollständige Spezifikation eine präzise Entsprechung zwischen dem vorgeschlagenen syntaktischen Zucker und den zugrunde liegenden bedingten Ausdrücken liefern, die ausreichend ist, um die Erstellung einer Referenzimplementierung zu leiten.

…wird noch ausgearbeitet…

Implementierung

Wie bei PEP 505 wurde die tatsächliche Implementierung bis zum Interesse im Prinzip an der Idee, diese Operatoren hinzuzufügen, zurückgestellt - die Implementierung ist nicht der schwierige Teil dieser Vorschläge, der schwierige Teil ist die Entscheidung, ob dies eine Änderung ist, bei der die langfristigen Vorteile für neue und bestehende Python-Benutzer die kurzfristigen Kosten für das breitere Ökosystem (einschließlich Entwickler anderer Implementierungen, Entwickler von Sprachlehrplänen und Autoren anderer Python-bezogener Lehrmaterialien) überwiegen, die sich an die Änderung anpassen müssen.

…wird noch ausgearbeitet…

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-0531.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT