PEP 535 – Rich comparison chaining
- Autor:
- Alyssa Coghlan <ncoghlan at gmail.com>
- Status:
- Verschoben
- Typ:
- Standards Track
- Benötigt:
- 532
- Erstellt:
- 12-Nov-2016
- Python-Version:
- 3.8
PEP Verschiebung
Weitere Überlegungen zu diesem PEP wurden frühestens bis Python 3.8 zurückgestellt.
Zusammenfassung
Inspiriert von PEP 335 und aufbauend auf dem in PEP 532 beschriebenen Circuit-Breaking-Protokoll schlägt dieses PEP eine Änderung der Definition von verketteten Vergleichen vor, bei der die Vergleichsketten aktualisiert werden, um den linksassoziativen Circuit-Breaking-Operator (else) anstelle des logischen Disjunktionsoperators (and) zu verwenden, wenn der linke Vergleich einen Circuit Breaker als Ergebnis liefert.
Während es einige praktische Komplexitäten gibt, die sich aus der aktuellen Handhabung von eindimensionalen Arrays in NumPy ergeben, sollte diese Änderung ausreichen, um elementweise verkettete Vergleichsoperationen für Matrizen zu ermöglichen, bei denen das Ergebnis eine Matrix boolescher Werte ist, anstatt ValueError auszulösen oder tautologisch True zurückzugeben (was eine nicht-leere Matrix anzeigt).
Beziehung zu anderen PEPs
Dieses PEP wurde aus früheren Iterationen von PEP 532 extrahiert, als Folgeanwendung für das Circuit-Breaking-Protokoll und nicht als wesentlicher Bestandteil seiner Einführung.
Der spezifische Vorschlag in diesem PEP, den elementweisen Vergleichs-Use-Case durch Änderung der semantischen Definition der Vergleichsketten zu behandeln, stammt direkt aus Guidos Ablehnung von PEP 335.
Spezifikation
Ein verketteter Vergleich wie 0 < x < 10, geschrieben als
LEFT_BOUND LEFT_OP EXPR RIGHT_OP RIGHT_BOUND
ist derzeit ungefähr semantisch äquivalent zu
_expr = EXPR
_lhs_result = LEFT_BOUND LEFT_OP _expr
_expr_result = _lhs_result and (_expr RIGHT_OP RIGHT_BOUND)
Unter Verwendung der in PEP 532 eingeführten Circuit-Breaking-Konzepte schlägt dieses PEP vor, die Vergleichsketten zu ändern, um explizit zu prüfen, ob der linke Vergleich einen Circuit Breaker zurückgibt, und falls dies der Fall ist, else anstelle von and zu verwenden, um die Vergleichsketten zu implementieren.
_expr = EXPR
_lhs_result = LEFT_BOUND LEFT_OP _expr
if hasattr(type(_lhs_result), "__else__"):
_expr_result = _lhs_result else (_expr RIGHT_OP RIGHT_BOUND)
else:
_expr_result = _lhs_result and (_expr RIGHT_OP RIGHT_BOUND)
Dies ermöglicht es Typen wie NumPy-Arrays, das Verhalten von verketteten Vergleichen zu steuern, indem sie entsprechend definierte Circuit Breaker von Vergleichsoperationen zurückgeben.
Die Erweiterung dieser Logik auf eine beliebige Anzahl von verketteten Vergleichsoperationen wäre dieselbe wie die bestehende Erweiterung für and.
Begründung
Bei der endgültigen Ablehnung von PEP 335 stellte Guido van Rossum fest [1]
Die NumPy-Leute brachten ein etwas separates Problem auf: Für sie ist der häufigste Anwendungsfall verkettete Vergleiche (z. B. A < B < C).
Um diese Beobachtung zu verstehen, müssen wir zuerst untersuchen, wie Vergleiche mit NumPy-Arrays funktionieren
>>> import numpy as np
>>> increasing = np.arange(5)
>>> increasing
array([0, 1, 2, 3, 4])
>>> decreasing = np.arange(4, -1, -1)
>>> decreasing
array([4, 3, 2, 1, 0])
>>> increasing < decreasing
array([ True, True, False, False, False], dtype=bool)
Hier sehen wir, dass NumPy-Array-Vergleiche standardmäßig elementweise erfolgen, wobei jedes Element im linken Array mit dem entsprechenden Element im rechten Array verglichen wird und eine Matrix mit booleschen Ergebnissen erzeugt wird.
Wenn eine Seite des Vergleichs ein Skalarwert ist, wird er über das Array ausgestrahlt und mit jedem einzelnen Element verglichen.
>>> 0 < increasing
array([False, True, True, True, True], dtype=bool)
>>> increasing < 4
array([ True, True, True, True, False], dtype=bool)
Dieses Broadcasting-Idiom bricht jedoch zusammen, wenn wir versuchen, verkettete Vergleiche zu verwenden.
>>> 0 < increasing < 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Das Problem ist, dass Python diese verkettete Vergleichsoperation intern implizit in die Form expandiert:
>>> 0 < increasing and increasing < 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Und NumPy erlaubt die implizite Konvertierung in einen booleschen Wert nur für Arrays mit einem einzigen Element, bei denen a.any() und a.all() garantiert das gleiche Ergebnis liefern.
>>> np.array([False]) and np.array([False])
array([False], dtype=bool)
>>> np.array([False]) and np.array([True])
array([False], dtype=bool)
>>> np.array([True]) and np.array([False])
array([False], dtype=bool)
>>> np.array([True]) and np.array([True])
array([ True], dtype=bool)
Der in diesem PEP vorgeschlagene Ansatz würde es ermöglichen, diese Situation zu ändern, indem die Definition von elementweisen Vergleichsoperationen in NumPy aktualisiert wird, um eine dedizierte Unterklasse zurückzugeben, die das neue Circuit-Breaking-Protokoll implementiert und auch die Interpretation des Ergebnis-Arrays in einem booleschen Kontext ändert, um immer False zurückzugeben und somit niemals das Short-Circuiting-Verhalten auszulösen.
class ComparisonResultArray(np.ndarray):
def __bool__(self):
# Element-wise comparison chaining never short-circuits
return False
def _raise_NotImplementedError(self):
msg = ("Comparison array truth values are ambiguous outside "
"chained comparisons. Use a.any() or a.all()")
raise NotImplementedError(msg)
def __not__(self):
self._raise_NotImplementedError()
def __then__(self, result):
self._raise_NotImplementedError()
def __else__(self, result):
return np.logical_and(self, other.view(ComparisonResultArray))
Mit dieser Änderung könnte das obige Beispiel für verkettete Vergleiche zurückgeben:
>>> 0 < increasing < 4
ComparisonResultArray([ False, True, True, True, False], dtype=bool)
Implementierung
Die tatsächliche Implementierung wurde zurückgestellt, bis ein prinzipielles Interesse an der Idee besteht, die in PEP 532 vorgeschlagenen Änderungen umzusetzen.
…wird noch ausgearbeitet…
Referenzen
Urheberrecht
Dieses Dokument wurde gemäß den Bedingungen der CC0 1.0 Lizenz in die Public Domain gestellt: https://creativecommons.org/publicdomain/zero/1.0/
Quelle: https://github.com/python/peps/blob/main/peps/pep-0535.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT