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

Python Enhancement Proposals

PEP 225 – Elementweise/Objektweise Operatoren

Autor:
Huaiyu Zhu <hzhu at users.sourceforge.net>, Gregory Lielens <gregory.lielens at fft.be>
Status:
Abgelehnt
Typ:
Standards Track
Erstellt:
19-Sep-2000
Python-Version:
2.1
Post-History:


Inhaltsverzeichnis

Warnung

Dieser PEP wurde abgelehnt.

×

Der Ansatz im späteren PEP 465 wurde schließlich anstelle dieses PEPs akzeptiert. Die Abgelehnte Ideen dieses PEPs erklären die Begründung ausführlicher.

Einleitung

Dieses PEP beschreibt einen Vorschlag zur Einführung neuer Operatoren in Python, die für die Unterscheidung von elementweisen und objektweisen Operationen nützlich sind, und fasst die Diskussionen in der Nachrichtengruppe comp.lang.python zu diesem Thema zusammen. Siehe Abschnitt Danksagungen und Archive am Ende. Hier besprochene Themen umfassen

  • Hintergrund.
  • Beschreibung der vorgeschlagenen Operatoren und Implementierungsfragen.
  • Analyse von Alternativen zu neuen Operatoren.
  • Analyse alternativer Formen.
  • Kompatibilitätsprobleme
  • Beschreibung breiterer Erweiterungen und anderer verwandter Ideen.

Ein beträchtlicher Teil dieses PEPs beschreibt Ideen, die nicht in die vorgeschlagene Erweiterung einfließen. Sie werden präsentiert, da die Erweiterung im Wesentlichen syntaktischer Zucker ist, so dass ihre Annahme gegen verschiedene mögliche Alternativen abgewogen werden muss. Während viele Alternativen in einigen Aspekten besser sein mögen, erscheint der aktuelle Vorschlag insgesamt vorteilhaft.

Die Fragen bezüglich elementweiser-objektweiser Operationen erstrecken sich auf breitere Bereiche als die numerische Berechnung. Dieses Dokument beschreibt auch, wie der aktuelle Vorschlag mit allgemeineren zukünftigen Erweiterungen integriert werden kann.

Hintergrund

Python bietet sechs binäre Infix-Mathematikoperatoren: + - * / % **, die im Folgenden generisch durch op dargestellt werden. Sie können mit neuer Semantik für benutzerdefinierte Klassen überladen werden. Bei Objekten, die aus homogenen Elementen bestehen, wie Arrays, Vektoren und Matrizen in der numerischen Berechnung, gibt es jedoch zwei im Wesentlichen unterschiedliche Arten von Semantik. Die objektweisen Operationen behandeln diese Objekte als Punkte in mehrdimensionalen Räumen. Die elementweisen Operationen behandeln sie als Sammlungen einzelner Elemente. Diese beiden Arten von Operationen sind oft in denselben Formeln vermischt, was eine syntaktische Unterscheidung erfordert.

Viele Sprachen für numerische Berechnungen bieten zwei Sätze von Mathematikoperatoren. Zum Beispiel wird in MatLab der gewöhnliche op für objektweise Operationen verwendet, während .op für elementweise Operationen verwendet wird. In R steht op für elementweise Operationen, während %op% für objektweise Operationen steht.

In Python gibt es andere Darstellungsformen, von denen einige bereits von verfügbaren numerischen Paketen verwendet werden, wie z. B.

  • Funktion: mul(a,b)
  • Methode: a.mul(b)
  • Casting: a.E*b

In mehreren Aspekten sind diese nicht so gut wie Infix-Operatoren. Mehr Details werden später gezeigt, aber die Kernpunkte sind

  • Lesbarkeit: Selbst für mäßig komplizierte Formeln sind Infix-Operatoren viel sauberer als Alternativen.
  • Vertrautheit: Benutzer sind mit gewöhnlichen Mathematikoperatoren vertraut.
  • Implementierung: Neue Infix-Operatoren werden die Python-Syntax nicht unnötig überladen. Sie werden die Implementierung numerischer Pakete erheblich erleichtern.

Obwohl es möglich ist, aktuelle Mathematikoperatoren einer Art von Semantik zuzuweisen, gibt es einfach nicht genügend Infix-Operatoren, um sie für die andere Art zu überladen. Es ist auch unmöglich, die visuelle Symmetrie zwischen diesen beiden Arten beizubehalten, wenn eine von ihnen keine Symbole für gewöhnliche Mathematikoperatoren enthält.

Vorgeschlagene Erweiterung

  • Sechs neue binäre Infix-Operatoren ~+ ~- ~* ~/ ~% ~** werden zu Python hinzugefügt. Sie spiegeln die bestehenden Operatoren + - * / % ** wider.
  • Sechs erweiterte Zuweisungsoperatoren ~+= ~-= ~*= ~/= ~%= ~**= werden zu Python hinzugefügt. Sie spiegeln die Operatoren += -= *= /= %= **= wider, die in Python 2.0 verfügbar sind.
  • Der Operator ~op behält die syntaktischen Eigenschaften des Operators op, einschließlich der Präzedenz.
  • Der Operator ~op behält die semantischen Eigenschaften des Operators op für integrierte Zahlentypen.
  • Der Operator ~op löst bei nicht-numerischen integrierten Typen einen Syntaxfehler aus. Dies ist vorläufig, bis das richtige Verhalten vereinbart werden kann.
  • Diese Operatoren sind in Klassen mit Namen überladbar, die ein *t* (für Tilde) vor den Namen gewöhnlicher Mathematikoperatoren stellen. Zum Beispiel funktionieren __tadd__ und __rtadd__ für ~+ genauso wie __add__ und __radd__ für + funktionieren.
  • Wie bei bestehenden Operatoren werden die Methoden __r*__() aufgerufen, wenn der linke Operand die entsprechende Methode nicht bereitstellt.

Es ist beabsichtigt, dass ein Satz von op oder ~op für elementweise Operationen und der andere für objektweise Operationen verwendet wird. Es ist jedoch nicht spezifiziert, welche Version der Operatoren für elementweise oder objektweise Operationen steht, wodurch die Entscheidung den Anwendungen überlassen wird.

Die vorgeschlagene Implementierung besteht darin, mehrere Dateien zu patchen, die sich auf den Tokenizer, Parser, die Grammatik und den Compiler beziehen, um die Funktionalität der entsprechenden bestehenden Operatoren nach Bedarf zu duplizieren. Alle neuen Semantiken sollen in den Klassen implementiert werden, die sie überladen.

Das Symbol ~ wird in Python bereits als unärer *Bitwise Not*-Operator verwendet. Derzeit ist es für binäre Operatoren nicht erlaubt. Die neuen Operatoren sind vollständig abwärtskompatibel.

Prototyp-Implementierung

Greg Lielens implementierte den Infix-Operator ~op als Patch gegen den Quellcode von Python 2.0b1 [1].

Um ~ als Teil von binären Operatoren zu erlauben, würde der Tokenizer ~+ als ein Token behandeln. Das bedeutet, dass der derzeit gültige Ausdruck ~+1 als ~+ 1 und nicht als ~ + 1 tokenisiert würde. Der Parser würde ~+ dann als eine Zusammensetzung aus ~ + behandeln. Die Auswirkung ist für Anwendungen unsichtbar.

Hinweise zum aktuellen Patch

  • Er beinhaltet noch keine ~op=-Operatoren.
  • Der ~op verhält sich bei Listen genauso wie op, anstatt Ausnahmen auszulösen.

Diese sollten behoben werden, wenn die endgültige Version dieses Vorschlags fertig ist.

  • Er reserviert xor als Infix-Operator mit der Semantik, die äquivalent ist zu
    def __xor__(a, b):
        if not b: return a
        elif not a: return b
        else: 0
    

Dies bewahrt den tatsächlichen Wert so weit wie möglich, andernfalls wird der linke Teilwert, wenn möglich, bewahrt.

Dies geschieht, damit bitweise Operatoren zukünftig als elementweise logische Operatoren betrachtet werden können (siehe unten).

Alternativen zur Einführung neuer Operatoren

Die Diskussionen auf comp.lang.python und der Mailingliste python-dev haben viele Alternativen erforscht. Einige der führenden Alternativen sind hier aufgelistet, wobei der Multiplikationsoperator als Beispiel dient.

  1. Verwendung der Funktion mul(a,b).

    Vorteil

    • Keine Notwendigkeit für neue Operatoren.

    Nachteil

    • Präfix-Formen sind für zusammengesetzte Formeln umständlich.
    • Für die beabsichtigten Benutzer ungewohnt.
    • Zu wortreich für die beabsichtigten Benutzer.
    • Kann keine natürlichen Präzedenzregeln verwenden.
  2. Verwendung des Methodenaufrufs a.mul(b).

    Vorteil

    • Keine Notwendigkeit für neue Operatoren.

    Nachteil

    • Asymmetrisch für beide Operanden.
    • Für die beabsichtigten Benutzer ungewohnt.
    • Zu wortreich für die beabsichtigten Benutzer.
    • Kann keine natürlichen Präzedenzregeln verwenden.
  3. Verwendung von *Schattenklassen*. Für eine Matrixklasse eine Schatten-Array-Klasse definieren, die über eine Methode .E zugänglich ist, so dass für Matrizen *a* und *b* a.E*b ein Matrixobjekt wäre, das elementwise_mul(a,b) ist.

    Ebenso eine Schatten-Matrixklasse für Arrays definieren, die über eine Methode .M zugänglich ist, so dass für Arrays *a* und *b* a.M*b ein Array wäre, das matrixwise_mul(a,b) ist.

    Vorteil

    • Keine Notwendigkeit für neue Operatoren.
    • Vorteile von Infix-Operatoren mit korrekten Präzedenzregeln.
    • Saubere Formeln in Anwendungen.

    Nachteil

    • Schwer zu warten im aktuellen Python, da gewöhnliche Zahlen keine benutzerdefinierten Klassenmethoden haben können; d. h. a.E*b schlägt fehl, wenn a eine reine Zahl ist.
    • Schwer zu implementieren, da dies bestehende Methodenaufrufe stört, wie z. B. .T für Transponierung usw.
    • Laufzeit-Overhead für Objekterstellung und Methodenaufruf.
    • Die Schattenklasse kann keine echte Klasse ersetzen, da sie nicht ihren eigenen Typ zurückgibt. Daher muss es eine M-Klasse mit einer Schatten-E-Klasse und eine E-Klasse mit einer Schatten-M-Klasse geben.
    • Unnatürlich für Mathematiker.
  4. Implementierung von Matrix- und Element-Klassen mit einfachem Casting zur anderen Klasse. Matrixweise Operationen für Arrays wären also wie a.M*b.M und elementweise Operationen für Matrizen wie a.E*b.E. Zur Fehlererkennung würde a.E*b.M Ausnahmen auslösen.

    Vorteil

    • Keine Notwendigkeit für neue Operatoren.
    • Ähnlich wie Infix-Notation mit korrekten Präzedenzregeln.

    Nachteil

    • Ähnliche Schwierigkeit aufgrund des Fehlens von Benutzer-Methoden für reine Zahlen.
    • Laufzeit-Overhead für Objekterstellung und Methodenaufruf.
    • Mehr überladene Formeln.
    • Das Umschalten des Objektflügels zur Erleichterung der Operatoren wird persistent. Dies führt zu Langzeitkontextabhängigkeiten im Anwendungscode, die extrem schwer zu warten wären.
  5. Verwendung eines Mini-Parsers zum Parsen von Formeln, die in beliebigen Erweiterungen in Anführungszeichen geschrieben sind.

    Vorteil

    • Reines Python, ohne neue Operatoren

    Nachteil

    • Die eigentliche Syntax befindet sich innerhalb der Anführungszeichen, was das Problem selbst nicht löst.
    • Einführung von Zonen mit spezieller Syntax.
    • Anspruchsvoll an den Mini-Parser.
  6. Einführung eines einzigen Operators, wie z. B. @, für die Matrixmultiplikation.

    Vorteil

    • Führt weniger Operatoren ein

    Nachteil

    • Die Unterscheidungen für Operatoren wie + - ** sind ebenso wichtig. Ihre Bedeutung in matrix- oder array-orientierten Paketen würde umgekehrt (siehe unten).
    • Der neue Operator belegt ein Sonderzeichen.
    • Dies funktioniert nicht gut mit allgemeineren Objekt-Element-Problemen.

Unter diesen Alternativen werden die erste und die zweite in aktuellen Anwendungen bis zu einem gewissen Grad verwendet, aber als unzureichend befunden. Die dritte ist die beliebteste für Anwendungen, aber sie wird enorme Implementierungskomplexität mit sich bringen. Die vierte würde Anwendungscodes sehr kontextsensitiv und schwer zu warten machen. Diese beiden Alternativen teilen auch erhebliche Implementierungsschwierigkeiten aufgrund der aktuellen Typ-/Klassenaufteilung. Die fünfte scheint mehr Probleme zu schaffen, als sie lösen würde. Die sechste deckt nicht den gleichen Anwendungsbereich ab.

Alternative Formen von Infix-Operatoren

Zwei Hauptformen und mehrere Nebenvarianten von neuen Infix-Operatoren wurden diskutiert

  • Klammerform
    (op)
    [op]
    {op}
    <op>
    :op:
    ~op~
    %op%
    
  • Meta-Zeichen-Form
    .op
    @op
    ~op
    

    Alternativ wird das Meta-Zeichen nach dem Operator platziert.

  • Weniger konsistente Variationen dieser Themen. Diese werden ungünstig betrachtet. Vollständigkeitshalber sind einige hier aufgelistet
    • Verwendung von @/ und /@ für Links- und Rechtsdivision
    • Verwendung von [*] und (*) für Außen- und Innenprodukt
    • Verwendung eines einzelnen Operators @ für die Multiplikation.
  • Verwendung von __call__ zur Simulation der Multiplikation
    a(b) or (a)(b)
    

Kriterien für die Auswahl unter den Darstellungen sind

  • Keine syntaktischen Mehrdeutigkeiten mit bestehenden Operatoren.
  • Höhere Lesbarkeit in tatsächlichen Formeln. Dies macht die Klammerformen ungünstig. Siehe Beispiele unten.
  • Visuell den bestehenden Mathematikoperatoren ähnlich.
  • Syntaktisch einfach, ohne mögliche zukünftige Erweiterungen zu blockieren.

Mit diesen Kriterien ist der Gesamtsieger in Klammerform {op}. Ein klarer Sieger in der Meta-Zeichen-Form ist ~op. Beim Vergleich dieser beiden scheint ~op der Favorit unter allen zu sein.

Einige Analysen sind wie folgt

  • Die Form .op ist mehrdeutig: 1.+a wäre anders als 1 .+a.
  • Die Klammertyp-Operatoren sind am günstigsten, wenn sie allein stehen, aber nicht in Formeln, da sie die visuelle Analyse von Klammern für Präzedenz und Funktionsargumente stören. Dies gilt für (op) und [op], und etwas weniger für {op} und <op>.
  • Die Form <op> hat das Potenzial, mit < > und = verwechselt zu werden.
  • @op wird nicht bevorzugt, da @ visuell schwerfällig ist (dicht, eher wie ein Buchstabe): a@+b wird leichter als a@ + b denn als a @+ b gelesen.
  • Für die Wahl von Meta-Zeichen: Die meisten vorhandenen ASCII-Symbole wurden bereits verwendet. Die einzigen drei ungenutzten sind @ $ ?.

Semantik der neuen Operatoren

Es gibt überzeugende Argumente für die Verwendung beider Sätze von Operatoren als objektweise oder elementweise. Einige davon sind hier aufgelistet

  1. op für Elemente, ~op für Objekte
    • Konsistent mit der aktuellen Multiarray-Schnittstelle des Numeric-Pakets.
    • Konsistent mit einigen anderen Sprachen.
    • Wahrnehmung, dass elementweise Operationen natürlicher sind.
    • Wahrnehmung, dass elementweise Operationen häufiger verwendet werden
  2. op für Objekte, ~op für Elemente
    • Konsistent mit der aktuellen Lineare-Algebra-Schnittstelle des MatPy-Pakets.
    • Konsistent mit einigen anderen Sprachen.
    • Wahrnehmung, dass objektweise Operationen natürlicher sind.
    • Wahrnehmung, dass objektweise Operationen häufiger verwendet werden.
    • Konsistent mit dem aktuellen Verhalten von Operatoren für Listen.
    • Erlaubt ~ als allgemeines elementweises Meta-Zeichen für zukünftige Erweiterungen.

Es herrscht allgemein Einigkeit darüber, dass

  • Es keinen absoluten Grund gibt, das eine dem anderen vorzuziehen.
  • Es einfach ist, von einer Darstellung zur anderen in einem beträchtlichen Teil des Codes zu konvertieren, so dass die andere Art von Operatoren immer die Minderheit ist.
  • Es andere semantische Unterschiede gibt, die für die Existenz von Array-orientierten und Matrix-orientierten Paketen sprechen, auch wenn ihre Operatoren vereinheitlicht sind.
  • Welche Entscheidung auch immer getroffen wird, Codes, die bestehende Schnittstellen verwenden, sollten nicht sehr lange defekt sein.

Daher geht nicht viel verloren, und es bleibt viel Flexibilität erhalten, wenn die semantischen Ausprägungen dieser beiden Sätze von Operatoren nicht von der Kernsprache diktiert werden. Die Anwendungspakete sind dafür verantwortlich, die am besten geeignete Wahl zu treffen. Dies ist bereits bei NumPy und MatPy der Fall, die entgegengesetzte Semantik verwenden. Das Hinzufügen neuer Operatoren wird dies nicht brechen. Siehe auch die Beobachtung nach Unterabschnitt 2 in den Beispielen unten.

Die Frage der numerischen Präzision wurde aufgeworfen, aber wenn die Semantik den Anwendungen überlassen wird, sollte die tatsächliche Präzision ebenfalls dorthin gehen.

Beispiele

Folgend sind Beispiele für tatsächliche Formeln, die mit verschiedenen Operatoren oder anderen oben beschriebenen Darstellungen erscheinen werden.

  1. Die Formel für die Matrixinversion
    • Verwendung von op für Objekte und ~op für Elemente
      b = a.I - a.I * u / (c.I + v/a*u) * v / a
      
      b = a.I - a.I * u * (c.I + v*a.I*u).I * v * a.I
      
    • Verwendung von op für Elemente und ~op für Objekte
      b = a.I @- a.I @* u @/ (c.I @+ v@/a@*u) @* v @/ a
      
      b = a.I ~- a.I ~* u ~/ (c.I ~+ v~/a~*u) ~* v ~/ a
      
      b = a.I (-) a.I (*) u (/) (c.I (+) v(/)a(*)u) (*) v (/) a
      
      b = a.I [-] a.I [*] u [/] (c.I [+] v[/]a[*]u) [*] v [/] a
      
      b = a.I <-> a.I <*> u </> (c.I <+> v</>a<*>u) <*> v </> a
      
      b = a.I {-} a.I {*} u {/} (c.I {+} v{/}a{*}u) {*} v {/} a
      

    Beobachtung: Für die lineare Algebra ist die Verwendung von op für Objekte vorzuziehen.

    Beobachtung: Die ~op-Typ-Operatoren sehen besser aus als (op)-Typen in komplizierten Formeln.

    • Verwendung benannter Operatoren
      b = a.I @sub a.I @mul u @div (c.I @add v @div a @mul u) @mul v @div a
      
      b = a.I ~sub a.I ~mul u ~div (c.I ~add v ~div a ~mul u) ~mul v ~div a
      

    Beobachtung: Benannte Operatoren sind für mathematische Formeln nicht geeignet.

  2. Plotten eines 3D-Graphen
    • Verwendung von op für Objekte und ~op für Elemente
      z = sin(x~**2 ~+ y~**2);    plot(x,y,z)
      
    • Verwendung von op für Elemente und ~op für Objekte
      z = sin(x**2 + y**2);   plot(x,y,z)
      

    Beobachtung: Elementweise Operationen mit Broadcasting ermöglichen eine viel effizientere Implementierung als MatLab.

    Beobachtung: Es ist nützlich, zwei verwandte Klassen mit der Semantik von op und ~op vertauscht zu haben. Mit diesen müssten die ~op-Operatoren nur in Codeabschnitten erscheinen, in denen die andere Art dominiert, während die konsistente Semantik des Codes beibehalten wird.

  3. Verwendung von + und - mit automatischem Broadcasting
    a = b - c;  d = a.T*a
    

    Beobachtung: Dies würde stillschweigend zu schwer zu verfolgenden Fehlern führen, wenn einer von *b* oder *c* ein Zeilenvektor und der andere ein Spaltenvektor ist.

Verschiedene Themen

  • Bedarf an den Operatoren ~+ ~-. Die objektweisen + - sind wichtig, da sie wichtige Überprüfungen gemäß der Linearen Algebra bieten. Die elementweisen + - sind wichtig, da sie Broadcasting ermöglichen, das in Anwendungen sehr effizient ist.
  • Linke Division (lösen). Für eine Matrix ist a*x nicht unbedingt gleich x*a. Die Lösung von a*x==b, bezeichnet als x=solve(a,b), ist daher anders als die Lösung von x*a==b, bezeichnet als x=div(b,a). Es gibt Diskussionen über die Suche nach einem neuen Symbol für "solve". [Hintergrund: MatLab verwendet b/a für div(b,a) und a\b für solve(a,b).]

    Es wird erkannt, dass Python eine bessere Lösung bietet, ohne ein neues Symbol zu benötigen: Die Methode inverse .I kann so verzögert werden, dass a.I*b und b*a.I äquivalent zu Matlabs a\b und b/a sind. Die Implementierung ist recht einfach und der resultierende Anwendungscode sauber.

  • Potenzoperator. Pythons Verwendung von a**b als pow(a,b) hat zwei wahrgenommene Nachteile
    • Die meisten Mathematiker sind mit a^b für diesen Zweck vertrauter.
    • Es führt zu einem langen erweiterten Zuweisungsoperator ~**=.

    Dieses Thema ist jedoch vom Hauptthema hier getrennt.

  • Zusätzliche Multiplikationsoperatoren. Verschiedene Formen von Multiplikationen werden in der (Multi-)Linearen Algebra verwendet. Die meisten können als Variationen der Multiplikation im Sinne der Linearen Algebra betrachtet werden (z. B. Kronecker-Produkt). Zwei Formen scheinen jedoch grundlegender zu sein: Außenprodukt und Innenprodukt. Ihre Spezifikation beinhaltet jedoch Indizes, die entweder sein können
    • dem Operator zugeordnet, oder
    • den Objekten zugeordnet.

    Letzteres (die Einstein-Notation) wird auf Papier extensiv verwendet und ist auch einfacher zu implementieren. Durch die Implementierung einer Tensor-mit-Indizes-Klasse würde eine allgemeine Form der Multiplikation sowohl Außen- als auch Innenprodukte abdecken und sich auch auf die Lineare Algebra-Multiplikation spezialisieren. Die Indexregel kann als Klassenmethode definiert werden, wie

    a = b.i(1,2,-1,-2) * c.i(4,-2,3,-1)   # a_ijkl = b_ijmn c_lnkm
    

    Daher reicht eine objektweise Multiplikation aus.

  • Bitweise Operatoren.
    • Die vorgeschlagenen neuen Mathematikoperatoren verwenden das Symbol ~, das der *Bitwise Not*-Operator ist. Dies stellt kein Kompatibilitätsproblem dar, erschwert jedoch die Implementierung.
    • Das Symbol ^ könnte besser für pow als für das bitweise xor verwendet werden. Dies hängt jedoch von der Zukunft der bitweisen Operatoren ab. Es hat keine unmittelbaren Auswirkungen auf den vorgeschlagenen Mathematikoperator.
    • Das Symbol | wurde vorgeschlagen, um für Matrix-Solve verwendet zu werden. Die neue Lösung mit verzögertem .I ist jedoch in mehrfacher Hinsicht besser.
    • Der aktuelle Vorschlag passt in eine größere und allgemeinere Erweiterung, die die Notwendigkeit von speziellen bitweisen Operatoren beseitigen wird. (Siehe Elementarisierung unten.)
  • Alternative zu speziellen Operatornamen, die in der Definition verwendet werden,
    def "+"(a, b)      in place of       def __add__(a, b)
    

    Dies scheint größere syntaktische Änderungen zu erfordern und wäre nur nützlich, wenn beliebige zusätzliche Operatoren erlaubt wären.

Auswirkungen auf allgemeine Elementarisierung

Die Unterscheidung zwischen objektweisen und elementweisen Operationen ist auch in anderen Kontexten sinnvoll, in denen ein Objekt konzeptionell als Sammlung von Elementen betrachtet werden kann. Es ist wichtig, dass der aktuelle Vorschlag mögliche zukünftige Erweiterungen nicht ausschließt.

Eine allgemeine zukünftige Erweiterung besteht darin, ~ als Meta-Operator zu verwenden, um einen gegebenen Operator zu *elementisieren*. Mehrere Beispiele sind hier aufgeführt

  1. Bitweise Operatoren. Derzeit weist Python sechs Operatoren Bitoperationen zu: and (&), or (|), xor (^), complement (~), left shift (<<) und right shift (>>), mit ihren eigenen Präzedenzebenen.

    Unter ihnen können die Operatoren & | ^ ~ als elementweise Versionen von Gitteroperatoren betrachtet werden, die auf ganze Zahlen angewendet werden, die als Bitstrings betrachtet werden.

    5 and 6                # 6
    5 or 6                 # 5
    
    5 ~and 6               # 4
    5 ~or 6                # 7
    

    Diese können als allgemeine elementweise Gitteroperatoren betrachtet werden, die nicht auf Bits in ganzen Zahlen beschränkt sind.

    Um benannte Operatoren für xor ~xor zu haben, ist es notwendig, xor zu einem reservierten Wort zu machen.

  2. Listen-Arithmetik.
    [1, 2] + [3, 4]        # [1, 2, 3, 4]
    [1, 2] ~+ [3, 4]       # [4, 6]
    
    ['a', 'b'] * 2         # ['a', 'b', 'a', 'b']
    'ab' * 2               # 'abab'
    
    ['a', 'b'] ~* 2        # ['aa', 'bb']
    [1, 2] ~* 2            # [2, 4]
    

    Es ist auch konsistent mit dem kartesischen Produkt

    [1,2]*[3,4]            # [(1,3),(1,4),(2,3),(2,4)]
    
  3. List Comprehension.
    a = [1, 2]; b = [3, 4]
    ~f(a,b)                # [f(x,y) for x, y in zip(a,b)]
    ~f(a*b)                # [f(x,y) for x in a for y in b]
    a ~+ b                 # [x + y for x, y in zip(a,b)]
    
  4. Tupel-Generierung (die `zip`-Funktion in Python 2.0)
    [1, 2, 3], [4, 5, 6]   # ([1,2, 3], [4, 5, 6])
    [1, 2, 3]~,[4, 5, 6]   # [(1,4), (2, 5), (3,6)]
    
  5. Verwendung von ~ als generisches elementweises Meta-Zeichen zur Ersetzung von `map`
    ~f(a, b)               # map(f, a, b)
    ~~f(a, b)              # map(lambda *x:map(f, *x), a, b)
    

    Allgemeiner,

    def ~f(*x): return map(f, *x)
    def ~~f(*x): return map(~f, *x)
    ...
    
  6. Elementweiser Formatoperator (mit Broadcasting)
    a = [1,2,3,4,5]
    print ["%5d "] ~% a
    a = [[1,2],[3,4]]
    print ["%5d "] ~~% a
    
  7. Reichhaltige Vergleiche
    [1, 2, 3]  ~< [3, 2, 1]  # [1, 0, 0]
    [1, 2, 3] ~== [3, 2, 1]  # [0, 1, 0]
    
  8. Reichhaltige Indizierung
    [a, b, c, d] ~[2, 3, 1]  # [c, d, b]
    
  9. Tupel-Abflachung
    a = (1,2);  b = (3,4)
    f(~a, ~b)                # f(1,2,3,4)
    
  10. Kopieroperator
    a ~= b                   # a = b.copy()
    
Es kann spezifische Ebenen der Tiefenkopie geben
a ~~= b                  # a = b.copy(2)

Anmerkungen

  1. Es gibt wahrscheinlich viele andere ähnliche Situationen. Dieser allgemeine Ansatz scheint für die meisten von ihnen gut geeignet zu sein, anstelle mehrerer getrennter Erweiterungen für jede von ihnen (parallele und kreuzweise Iteration, List Comprehension, reiche Vergleiche usw.).
  2. Die Semantik von *elementweise* hängt von den Anwendungen ab. Zum Beispiel ist ein Element einer Matrix aus der Sicht einer Liste-in-Liste zwei Ebenen tiefer. Dies erfordert grundlegendere Änderungen als der aktuelle Vorschlag. In jedem Fall wird der aktuelle Vorschlag zukünftige Möglichkeiten dieser Art nicht negativ beeinflussen.

Beachten Sie, dass dieser Abschnitt eine Art von zukünftigen Erweiterungen beschreibt, die mit dem aktuellen Vorschlag konsistent sind, aber zusätzliche Kompatibilitäts- oder andere Probleme aufweisen können. Sie sind nicht an den aktuellen Vorschlag gebunden.

Auswirkungen auf benannte Operatoren

Die Diskussionen haben im Allgemeinen verdeutlicht, dass Infixoperatoren in Python eine knappe Ressource sind, nicht nur in der numerischen Berechnung, sondern auch in anderen Bereichen. Mehrere Vorschläge und Ideen wurden vorgebracht, die die Einführung von Infixoperatoren ähnlich wie benannte Funktionen ermöglichen würden. Wir zeigen hier, dass die aktuelle Erweiterung zukünftige Erweiterungen in dieser Hinsicht nicht negativ beeinflusst.

  1. Benannte Infixoperatoren.

    Wählen Sie ein Metazeichen, z. B. @, so dass für jeden Bezeichner opname die Kombination @opname ein binärer Infixoperator wäre, und

    a @opname b == opname(a,b)
    

    Andere erwähnte Darstellungen umfassen

    .name ~name~ :name: (.name) %name%
    

    und ähnliche Variationen. Reine klammerbasierte Operatoren können nicht auf diese Weise verwendet werden.

    Dies erfordert eine Änderung im Parser, um @opname zu erkennen und in dieselbe Struktur wie ein Funktionsaufruf zu parsen. Die Präzedenz aller dieser Operatoren müsste auf einer Ebene festgelegt werden, daher wäre die Implementierung anders als bei zusätzlichen mathematischen Operatoren, die die Präzedenz bestehender mathematischer Operatoren beibehalten.

    Die aktuell vorgeschlagene Erweiterung schränkt mögliche zukünftige Erweiterungen dieser Form in keiner Weise ein.

  2. Allgemeinere symbolische Operatoren.

    Eine zusätzliche Form zukünftiger Erweiterung ist die Verwendung von Metazeichen und Operatorsymbolen (Symbole, die in syntaktischen Strukturen außer Operatoren nicht verwendet werden können). Angenommen, @ ist das Metazeichen. Dann

    a + b,    a @+ b,    a @@+ b,  a @+- b
    

    wären alle Operatoren mit einer Präzedenzhierarchie, definiert durch

    def "+"(a, b)
    def "@+"(a, b)
    def "@@+"(a, b)
    def "@+-"(a, b)
    

    Ein Vorteil gegenüber benannten Operatoren ist die größere Flexibilität bei der Präzedenz, die entweder auf dem Metazeichen oder den normalen Operatorsymbolen basiert. Dies ermöglicht auch die Operatorzusammensetzung. Der Nachteil ist, dass sie eher wie "Zeilenrauschen" sind. In jedem Fall beeinträchtigt der aktuelle Vorschlag dessen zukünftige Möglichkeit nicht.

    Diese Arten von zukünftigen Erweiterungen sind möglicherweise nicht notwendig, wenn Unicode allgemein verfügbar wird.

    Beachten Sie, dass dieser Abschnitt die Kompatibilität der vorgeschlagenen Erweiterung mit möglichen zukünftigen Erweiterungen diskutiert. Die Wünschbarkeit oder Kompatibilität dieser anderen Erweiterungen selbst wird hier ausdrücklich nicht berücksichtigt.

Danksagungen und Archive

Die Diskussionen fanden hauptsächlich im Juli bis August 2000 in der Newsgruppe comp.lang.python und der Mailingliste python-dev statt. Es gibt insgesamt mehrere hundert Beiträge, die meisten können von diesen beiden Seiten abgerufen werden (und durch Suche nach dem Wort „operator“).

Die Namen der Mitwirkenden sind zu zahlreich, um sie hier zu erwähnen. Es genügt zu sagen, dass ein großer Teil der hier diskutierten Ideen nicht unsere eigenen ist.

Mehrere Schlüsselbeiträge (aus unserer Sicht), die bei der Navigation durch die Diskussionen helfen können, sind:

https://pythonlang.de/pipermail/python-list/2000-July/108893.html https://pythonlang.de/pipermail/python-list/2000-July/108777.html https://pythonlang.de/pipermail/python-list/2000-July/108848.html https://pythonlang.de/pipermail/python-list/2000-July/109237.html https://pythonlang.de/pipermail/python-list/2000-July/109250.html https://pythonlang.de/pipermail/python-list/2000-July/109310.html https://pythonlang.de/pipermail/python-list/2000-July/109448.html https://pythonlang.de/pipermail/python-list/2000-July/109491.html https://pythonlang.de/pipermail/python-list/2000-July/109537.html https://pythonlang.de/pipermail/python-list/2000-July/109607.html https://pythonlang.de/pipermail/python-list/2000-July/109709.html https://pythonlang.de/pipermail/python-list/2000-July/109804.html https://pythonlang.de/pipermail/python-list/2000-July/109857.html https://pythonlang.de/pipermail/python-list/2000-July/110061.html https://pythonlang.de/pipermail/python-list/2000-July/110208.html https://pythonlang.de/pipermail/python-list/2000-August/111427.html https://pythonlang.de/pipermail/python-list/2000-August/111558.html https://pythonlang.de/pipermail/python-list/2000-August/112551.html https://pythonlang.de/pipermail/python-list/2000-August/112606.html https://pythonlang.de/pipermail/python-list/2000-August/112758.html

https://pythonlang.de/pipermail/python-dev/2000-July/013243.html https://pythonlang.de/pipermail/python-dev/2000-July/013364.html https://pythonlang.de/pipermail/python-dev/2000-August/014940.html

Dies sind frühere Entwürfe dieses PEP

Es gibt einen alternativen PEP (offiziell PEP 211) von Greg Wilson mit dem Titel „Adding New Linear Algebra Operators to Python“.

Seine erste (und aktuelle) Version ist unter

Zusätzliche Referenzen


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

Zuletzt geändert: 2024-04-14 20:08:31 GMT