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

Python Enhancement Proposals

PEP 237 – Vereinheitlichung von langen und ganzen Zahlen

Autor:
Moshe Zadka, Guido van Rossum
Status:
Final
Typ:
Standards Track
Erstellt:
11. März 2001
Python-Version:
2.2
Post-History:
16. Mrz 2001, 14. Aug 2001, 23. Aug 2001

Inhaltsverzeichnis

Zusammenfassung

Python unterscheidet derzeit zwischen zwei Arten von ganzen Zahlen (ints): reguläre oder kurze ints, die durch die Größe eines C-long begrenzt sind (typischerweise 32 oder 64 Bit), und lange ints, die nur durch den verfügbaren Speicher begrenzt sind. Wenn Operationen mit kurzen ints Ergebnisse liefern, die nicht in ein C-long passen, lösen sie einen Fehler aus. Es gibt auch einige andere Unterschiede. Dieses PEP schlägt vor, die meisten Unterschiede in der Semantik abzuschaffen und die beiden Typen aus der Perspektive des Python-Benutzers zu vereinheitlichen.

Begründung

Viele Programme stellen fest, dass sie nachträglich mit größeren Zahlen umgehen müssen, und die Änderung von Algorithmen ist später umständlich. Dies kann die Leistung im Normalfall beeinträchtigen, wenn die gesamte Arithmetik mit langen ints durchgeführt wird, unabhängig davon, ob sie benötigt werden oder nicht.

Die Prozesswortgröße, die in der Sprache exponiert wird, beeinträchtigt die Portabilität. Beispielsweise sind Python-Quellcodedateien und .pyc-Dateien aufgrund dessen nicht zwischen 32-Bit- und 64-Bit-Maschinen portierbar.

Es gibt auch den allgemeinen Wunsch, unnötige Details vor dem Python-Benutzer zu verbergen, wenn sie für die meisten Anwendungen irrelevant sind. Ein Beispiel ist die Speicherzuweisung, die in C explizit, in Python aber automatisch erfolgt, was uns den Komfort von unbegrenzten Größen für Zeichenketten, Listen usw. bietet. Es ist sinnvoll, diesen Komfort auf Zahlen auszudehnen.

Neue Python-Programmierer (egal ob sie neu in der Programmierung im Allgemeinen sind oder nicht) haben eine Sache weniger zu lernen, bevor sie mit der Verwendung der Sprache beginnen können.

Implementierung

Ursprünglich wurden zwei alternative Implementierungen vorgeschlagen (eine von jedem Autor)

  1. Das Feld für ein C-long des Typs PyInt wird in ein
    union {
        long i;
        struct {
            unsigned long length;
            digit digits[1];
        } bignum;
    };
    

    Nur die n-1 niederwertigsten Bits des long haben eine Bedeutung; das oberste Bit ist immer gesetzt. Dies unterscheidet die union. Alle PyInt-Funktionen überprüfen dieses Bit, bevor sie entscheiden, welche Arten von Operationen verwendet werden sollen.

  2. Die bestehenden Typen für kurze und lange ints bleiben bestehen, aber Operationen geben einen langen int zurück, anstatt OverflowError auszulösen, wenn ein Ergebnis nicht als kurzer int dargestellt werden kann. Ein neuer Typ, integer, kann eingeführt werden, der ein abstrakter Basistyp ist, von dem sowohl der int als auch der long Implementierungstyp abgeleitet sind. Dies ist nützlich, damit Programme die Integer-Eigenschaft mit einem einzigen Test überprüfen können
    if isinstance(i, integer): ...
    

Nach einiger Überlegung wurde der zweite Implementierungsplan ausgewählt, da er wesentlich einfacher zu implementieren ist, auf der C-API-Ebene abwärtskompatibel ist und zusätzlich teilweise als Übergangsmaßnahme implementiert werden kann.

Inkompatibilitäten

Die folgenden Operationen haben (meist subtil) unterschiedliche Semantiken für kurze und lange Integer, und eine oder die andere muss irgendwie geändert werden. Dies soll eine erschöpfende Liste sein. Wenn Sie von einer anderen Operation wissen, die je nachdem, ob ein kurzer oder ein langer Integer mit demselben Wert übergeben wird, ein anderes Ergebnis liefert, schreiben Sie bitte an den zweiten Autor.

  • Derzeit lösen alle arithmetischen Operatoren für kurze ints mit Ausnahme von << einen OverflowError aus, wenn das Ergebnis nicht als kurzer int dargestellt werden kann. Dies wird geändert, um stattdessen einen langen int zurückzugeben. Die folgenden Operatoren können derzeit OverflowError auslösen: x+y, x-y, x*y, x**y, divmod(x, y), x/y, x%y und -x. (Die letzten vier können nur überlaufen, wenn der Wert -sys.maxint-1 involviert ist.)
  • Derzeit kann x<<n Bits für kurze ints verlieren. Dies wird geändert, um einen langen int zurückzugeben, der alle verschobenen Bits enthält, wenn das Zurückgeben eines kurzen ints Bits verlieren würde (wobei ein Vorzeichenwechsel als Sonderfall des Verlusts von Bits betrachtet wird).
  • Derzeit können hexadezimale und oktale Literale für kurze ints negative Werte angeben; zum Beispiel 0xffffffff == -1 auf einer 32-Bit-Maschine. Dies wird geändert, um 0xffffffffL (2**32-1) zu entsprechen.
  • Derzeit verhalten sich die String-Formatierungsoperatoren %u, %x, %X und %o sowie die eingebauten Funktionen hex() und oct() für negative Zahlen unterschiedlich: negative kurze ints werden als vorzeichenlose C-long formatiert, während negative lange ints mit einem Minuszeichen formatiert werden. Dies wird geändert, um in allen Fällen die Semantik von langen ints zu verwenden (aber ohne das nachgestellte L, das derzeit die Ausgabe von hex() und oct() für lange ints kennzeichnet). Beachten Sie, dass dies bedeutet, dass %u zu einem Alias für %d wird. Es wird schließlich entfernt.
  • Derzeit gibt die repr() eines langen ints eine Zeichenkette zurück, die mit L endet, während die repr() eines kurzen ints dies nicht tut. Das L wird entfernt; aber nicht vor Python 3.0.
  • Derzeit gibt eine Operation mit langen Operanden niemals einen kurzen int zurück. Dies *kann* sich ändern, da es einige Optimierungen ermöglicht. (In diesem Bereich wurden bisher keine Änderungen vorgenommen, und es sind auch keine geplant.)
  • Der Ausdruck type(x).__name__ hängt davon ab, ob x ein kurzer oder ein langer int ist. Da die Implementierungsalternative 2 gewählt wird, bleibt dieser Unterschied bestehen. (In Python 3.0 können wir *vielleicht* einen Trick anwenden, um den Unterschied zu verbergen, da es ärgerlich ist, den Unterschied für Benutzercode offenzulegen, und noch mehr, da der Unterschied zwischen den beiden Typen weniger sichtbar ist.)
  • Lange und kurze ints werden vom marshal-Modul sowie von den Modulen pickle und cPickle unterschiedlich behandelt. Dieser Unterschied bleibt bestehen (zumindest bis Python 3.0).
  • Kurze ints mit kleinen Werten (typischerweise zwischen -1 und 99 einschließlich) werden *interniert* – wann immer ein Ergebnis einen solchen Wert hat, wird ein vorhandener kurzer int mit demselben Wert zurückgegeben. Dies geschieht nicht für lange ints mit denselben Werten. Dieser Unterschied bleibt bestehen. (Da es keine Garantie für diese Internierung gibt, ist es fraglich, ob dies ein semantischer Unterschied ist – aber es kann Code existieren, der is für Vergleiche von kurzen ints verwendet und aufgrund dieser Internierung zufällig funktioniert. Solcher Code kann fehlschlagen, wenn er mit langen ints verwendet wird.)

Literale

Ein nachgestelltes L am Ende eines Ganzzahlliterals hat keine Bedeutung mehr und wird schließlich illegal. Der Compiler wählt den geeigneten Typ ausschließlich basierend auf dem Wert. (Bis Python 3.0 erzwingt es, dass das Literal ein langer int ist; aber Literale ohne nachgestelltes L können ebenfalls lange ints sein, wenn sie nicht als kurze ints dargestellt werden können.)

Eingebaute Funktionen

Die Funktion int() gibt je nach Wert des Arguments einen kurzen oder einen langen int zurück. In Python 3.0 ruft die Funktion long() die Funktion int() auf; vorher wird sie weiterhin das Ergebnis auf einen langen int erzwingen, aber ansonsten genauso funktionieren wie int(). Der eingebaute Name long bleibt in der Sprache erhalten, um den langen Implementierungstyp darzustellen (sofern er in Python 3.0 nicht vollständig eliminiert wird), aber die Verwendung der Funktion int() wird weiterhin empfohlen, da sie bei Bedarf automatisch einen langen int zurückgibt.

C API

Die C-API bleibt unverändert; C-Code muss sich weiterhin der Unterscheidung zwischen kurzen und langen ints bewusst sein. (Die C-API von Python 3.0 wird wahrscheinlich völlig inkompatibel sein.)

Die PyArg_Parse*() APIs akzeptieren bereits lange ints, solange sie sich im Bereich von C-ints oder longs befinden, sodass Funktionen, die C-int- oder long-Argumente entgegennehmen, sich keine Gedanken über die Verarbeitung von Python-longs machen müssen.

Übergang

Es gibt drei Hauptphasen für die Umstellung

  1. Kurze Int-Operationen, die derzeit OverflowError auslösen, geben stattdessen einen langen Int-Wert zurück. Dies ist die einzige Änderung in dieser Phase. Literale werden weiterhin zwischen kurzen und langen ints unterscheiden. Die anderen aufgelisteten semantischen Unterschiede (einschließlich des Verhaltens von <<) bleiben bestehen. Da diese Phase nur Situationen ändert, die derzeit OverflowError auslösen, wird angenommen, dass dies keinen bestehenden Code bricht. (Code, der von dieser Ausnahme abhängt, wäre zu kompliziert, um sich darum zu kümmern.) Für diejenigen, die sich um extreme Abwärtskompatibilität sorgen, ermöglicht eine Befehlszeilenoption (oder ein Aufruf des Warnings-Moduls), dass an dieser Stelle eine Warnung oder ein Fehler ausgegeben wird, dies ist jedoch standardmäßig deaktiviert.
  2. Die verbleibenden semantischen Unterschiede werden behandelt. In allen Fällen wird die Semantik von langen ints gelten. Da dies zu Rückwärtsinkompatibilitäten führt, die einigen alten Code brechen werden, erfordert diese Phase möglicherweise eine zukünftige Aussage und/oder Warnungen und eine verlängerte Übergangsphase. Das nachgestellte L wird weiterhin für lange ints bei der Eingabe und von repr() verwendet.
    1. Warnungen werden für Operationen aktiviert, die ihre numerischen Ergebnisse in Phase 2B ändern werden, insbesondere hex() und oct(), %u, %x, %X und %o, hex- und oct-Literale im (einschließlich) Bereich [sys.maxint+1, sys.maxint*2+1] und Linksverschiebungen, die Bits verlieren.
    2. Die neue Semantik für diese Operationen wird implementiert. Operationen, die andere Ergebnisse als zuvor liefern, geben *keine* Warnung aus.
  3. Das nachgestellte L wird aus repr() entfernt und bei der Eingabe illegal gemacht. (Wenn möglich, verschwindet der long-Typ vollständig.) Das nachgestellte L wird auch aus hex() und oct() entfernt.

Phase 1 wird in Python 2.2 implementiert.

Phase 2 wird schrittweise implementiert, 2A in Python 2.3 und 2B in Python 2.4.

Phase 3 wird in Python 3.0 implementiert (mindestens zwei Jahre nach der Veröffentlichung von Python 2.4).

OverflowWarning

Hier sind die Regeln, die die Warnungen leiten, die in Situationen generiert werden, die derzeit OverflowError auslösen. Dies gilt für die Übergangsphase 1. Historischer Hinweis: Obwohl Phase 1 in Python 2.2 und Phase 2A in Python 2.3 abgeschlossen wurden, bemerkte niemand, dass OverflowWarning in Python 2.3 immer noch generiert wurde. Es wurde schließlich in Python 2.4 deaktiviert. Die Python-eingebaute OverflowWarning und die entsprechende C-API PyExc_OverflowWarning werden in Python 2.4 nicht mehr generiert oder verwendet, bleiben aber für den (unwahrscheinlichen) Fall von Benutzercode bis Python 2.5 bestehen.

  • Eine neue Warnungskategorie wird eingeführt, OverflowWarning. Dies ist ein eingebauter Name.
  • Wenn ein Int-Ergebnis überläuft, wird eine OverflowWarning-Warnung ausgegeben, mit einem Nachrichtenargument, das die Operation angibt, z.B. „integer addition“. Dies kann möglicherweise eine Warnmeldung auf sys.stderr anzeigen, oder einen Fehler auslösen, alles gesteuert durch die -W Befehlszeilenoption und das warnings-Modul.
  • Die OverflowWarning-Warnung wird standardmäßig ignoriert.
  • Die OverflowWarning-Warnung kann wie alle Warnungen gesteuert werden, über die -W Befehlszeilenoption oder über den warnings.filterwarnings() Aufruf. Zum Beispiel
    python -Wdefault::OverflowWarning
    

    die OverflowWarning zum ersten Mal, wenn sie an einer bestimmten Quellcodezeile auftritt, anzeigen lassen, und

    python -Werror::OverflowWarning
    

    die OverflowWarning in eine Ausnahme umwandeln lassen, wann immer sie auftritt. Der folgende Code aktiviert die Warnung aus dem Programm heraus

    import warnings
    warnings.filterwarnings("default", "", OverflowWarning)
    

    Siehe die Python man-Seite für die -W-Option und die Dokumentation des warnings-Moduls für filterwarnings().

  • Wenn die OverflowWarning-Warnung in eine Ausnahme umgewandelt wird, wird OverflowError eingesetzt. Dies ist aus Gründen der Abwärtskompatibilität erforderlich.
  • Wenn die Warnung nicht in eine Ausnahme umgewandelt wird, wird das Ergebnis der Operation (z.B. x+y) neu berechnet, nachdem die Argumente in lange ints konvertiert wurden.

Beispiel

Wenn Sie einen langen int an eine C-Funktion oder eine eingebaute Operation übergeben, die einen Integer erwartet, wird er wie ein kurzer int behandelt, solange der Wert passt (aufgrund der Implementierung von PyArg_ParseTuple()). Wenn der lange Wert nicht passt, wird immer noch ein OverflowError ausgelöst. Zum Beispiel

def fact(n):
    if n <= 1:
    return 1
return n*fact(n-1)

A = "ABCDEFGHIJKLMNOPQ"
n = input("Gimme an int: ")
print A[fact(n)%17]

Für n >= 13 löst dies derzeit OverflowError aus (es sei denn, der Benutzer gibt ein nachgestelltes L als Teil seiner Eingabe ein), obwohl der berechnete Index immer im Bereich range(17) liegen würde. Mit dem neuen Ansatz wird dieser Code das Richtige tun: der Index wird als langer int berechnet, aber sein Wert liegt im Bereich.

Gelöste Probleme

Diese zuvor offenen Fragen sind gelöst worden.

  • hex() und oct(), angewendet auf lange ints, erzeugen weiterhin ein nachgestelltes L bis Python 3000. Der ursprüngliche Text war hier nicht klar, aber da es in Python 2.4 nicht geschah, wurde beschlossen, es so zu belassen. BDFL-Erklärung hier

    https://mail.python.org/pipermail/python-dev/2006-June/065918.html

  • Was tun mit sys.maxint? Beibehalten, da es immer noch relevant ist, wenn die Unterscheidung zwischen kurzen und langen ints noch relevant ist (z.B. bei der Inspektion des Typs eines Werts).
  • Sollen wir %u komplett entfernen? Entfernen.
  • Sollen wir vor << warnen, das ganze Zahlen nicht abschneidet? Ja.
  • Soll die Überlaufwarnung bei einer portablen Maximalgröße liegen? Nein.

Implementierung

Die Implementierungsarbeiten für die Python 2.x-Reihe sind abgeschlossen; Phase 1 wurde mit Python 2.2 veröffentlicht, Phase 2A mit Python 2.3 und Phase 2B wird mit Python 2.4 veröffentlicht (und ist bereits in CVS).


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

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