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

Python Enhancement Proposals

PEP 3137 – Immutable Bytes und Mutable Buffer

Autor:
Guido van Rossum <guido at python.org>
Status:
Final
Typ:
Standards Track
Erstellt:
26-Sep-2007
Python-Version:
3.0
Post-History:
26-Sep-2007, 30-Sep-2007

Inhaltsverzeichnis

Einleitung

Nach der Veröffentlichung von Python 3.0a1 mit einem veränderbaren Bytes-Typ wuchs der Druck, eine Möglichkeit zur Darstellung von unveränderlichen Bytes zu schaffen. Gregory P. Smith schlug einen Patch vor, der es ermöglichen würde, ein Bytes-Objekt vorübergehend unveränderlich zu machen, indem die Daten mit der neuen Buffer-API aus PEP 3118 gesperrt werden. Dies schien mir nicht der richtige Ansatz zu sein.

Jeffrey Yasskin bereitete dann mit Hilfe von Adam Hupp einen Patch vor, um den Bytes-Typ unveränderlich zu machen (durch grobes Entfernen aller veränderbaren APIs) und die daraus resultierenden Probleme in der Testsuite zu beheben. Dies zeigte, dass es nicht allzu viele Stellen gibt, die von der Veränderbarkeit von Bytes abhängen, mit Ausnahme von Code, der einen Rückgabewert aus kleinen Teilen aufbaut.

Bei der Durchdenkung der Konsequenzen und unter Berücksichtigung, dass die Verwendung des Array-Moduls als Ersatz für einen veränderbaren Bytes-Typ alles andere als ideal ist, und in Erinnerung an einen früheren Vorschlag von Talin, schlug ich vor, sowohl einen veränderbaren als auch einen unveränderlichen Bytes-Typ zu haben. (Dies wurde schon früher angesprochen, aber bis ich die Beweise von Jeffreys Patch sah, war ich nicht offen für den Vorschlag.)

Darüber hinaus wurde eine mögliche Implementierungsstrategie klar: Verwenden Sie die alte PyString-Implementierung, bereinigt um die Unterstützung für Locales und implizite Konvertierungen von/zu Unicode, für den unveränderlichen Bytes-Typ und behalten Sie die neue PyBytes-Implementierung als veränderbaren Bytes-Typ bei.

Die anschließende Diskussion machte deutlich, dass die Idee willkommen ist, aber präziser spezifiziert werden muss. Daher diese PEP.

Vorteile

Ein Vorteil der Existenz eines unveränderlichen Bytes-Typs ist, dass Codeobjekte diese verwenden können. Es ermöglicht auch die effiziente Erstellung von Hashtabellen mit Bytes als Schlüssel; dies kann nützlich sein beim Parsen von Protokollen wie HTTP oder SMTP, die auf Bytes basieren, die Text darstellen.

Die Portierung von Code, der Binärdaten (oder kodierten Text) in Python 2.x manipuliert, wird mit dem neuen Design einfacher sein als mit dem ursprünglichen 3.0-Design mit veränderbaren Bytes; ersetzen Sie einfach str durch bytes und ändern Sie '...' Literale in b'...' Literale.

Benennung

Ich schlage die folgenden Typnamen auf Python-Ebene vor

  • bytes ist ein unveränderliches Byte-Array (PyString)
  • bytearray ist ein veränderbares Byte-Array (PyBytes)
  • memoryview ist eine Byte-Ansicht auf ein anderes Objekt (PyMemory)

Der alte Typ namens buffer ist dem neuen Typ memoryview, der von PEP 3118 eingeführt wurde, so ähnlich, dass er redundant ist. Der Rest dieser PEP diskutiert nicht die Funktionalität von memoryview; er wird hier nur erwähnt, um die Abschaffung des alten buffer-Typs zu rechtfertigen. (Eine frühere Version dieser PEP schlug buffer als neuen Namen für PyBytes vor; letztendlich wurde dieser Name angesichts der vielen anderen Verwendungen des Wortes "buffer" als zu verwirrend erachtet.)

Obwohl es schließlich sinnvoll ist, die C-API-Namen zu ändern, behält diese PEP die alten C-API-Namen bei, die allen vertraut sein sollten.

Zusammenfassung

Hier ist eine einfache ASCII-Art-Tabelle, die die Typnamen in verschiedenen Python-Versionen zusammenfasst

+--------------+-------------+------------+--------------------------+
| C name       | 2.x    repr | 3.0a1 repr | 3.0a2               repr |
+--------------+-------------+------------+--------------------------+
| PyUnicode    | unicode u'' | str     '' | str                   '' |
| PyString     | str      '' | str8   s'' | bytes                b'' |
| PyBytes      | N/A         | bytes  b'' | bytearray bytearray(b'') |
| PyBuffer     | buffer      | buffer     | N/A                      |
| PyMemoryView | N/A         | memoryview | memoryview         <...> |
+--------------+-------------+------------+--------------------------+

Literal Notations

Die in Python 3.0a1 eingeführte b'...' Notation gibt ein unveränderliches Bytes-Objekt zurück, unabhängig von der verwendeten Variante. Um ein veränderbares Byte-Array zu erstellen, verwenden Sie bytearray(b'...') oder bytearray([...]). Letztere Form nimmt eine Liste von ganzen Zahlen im Bereich von 0 bis 255 entgegen.

Funktionalität

PEP 3118 Buffer API

Sowohl Bytes als auch Bytearray implementieren die PEP 3118 Buffer-API. Der Bytes-Typ implementiert nur schreibgeschützte Anfragen; der Bytearray-Typ erlaubt auch beschreibbare und daten-gesperrte Anfragen. Der Datentyp der Elemente ist immer 'B' (d. h. vorzeichenloser Byte).

Konstruktoren

Es gibt vier Formen von Konstruktoren, die sowohl für Bytes als auch für Bytearrays gelten

  • bytes(<bytes>), bytes(<bytearray>), bytearray(<bytes>), bytearray(<bytearray>): einfache Kopierkonstruktoren, mit dem Hinweis, dass bytes(<bytes>) sein (unveränderliches) Argument zurückgeben kann, aber bytearray(<bytearray>) immer eine Kopie erstellt.
  • bytes(<str>, <encoding>[, <errors>]), bytearray(<str>, <encoding>[, <errors>]): kodiert einen Text-String. Beachten Sie, dass die Methode str.encode() ein *unveränderliches* Bytes-Objekt zurückgibt. Das Argument <encoding> ist obligatorisch; <errors> ist optional. <encoding> und <errors> müssen, falls angegeben, Strings sein.
  • bytes(<memory view>), bytearray(<memory view>): erstellt ein Bytes- oder Bytearray-Objekt aus allem, das die PEP 3118 Buffer-API implementiert.
  • bytes(<iterable von ints>), bytearray(<iterable von ints>): erstellt ein Bytes- oder Bytearray-Objekt aus einem Strom von ganzen Zahlen im Bereich von 0 bis 255.
  • bytes(<int>), bytearray(<int>): erstellt ein null-initialisiertes Bytes- oder Bytearray-Objekt einer bestimmten Länge.

Vergleiche

Die Bytes- und Bytearray-Typen sind miteinander vergleichbar und sortierbar, so dass z. B. b’abc’ == bytearray(b’abc’) < b’abd’.

Der Vergleich eines der beiden Typen mit einem String-Objekt auf Gleichheit ergibt False, unabhängig vom Inhalt eines der Operanden. Ordinale Vergleiche mit Strings lösen TypeError aus. Dies entspricht den Standardregeln für Vergleiche und Sortierungen zwischen Objekten inkompatibler Typen.

(**Hinweis:** In Python 3.0a1 würde der Vergleich einer Bytes-Instanz mit einer String-Instanz TypeError auslösen, mit der Begründung, dass dies gelegentliche Fehler schneller erkennen würde, insbesondere in Code, der aus Python 2.x portiert wurde. Eine lange Diskussion auf der python-3000-Liste wies jedoch auf so viele Probleme damit hin, dass dies eindeutig eine schlechte Idee ist und in 3.0a2 zurückgerollt werden sollte, unabhängig vom Schicksal des Rests dieser PEP.)

Slicing

Das Slicing eines Bytes-Objekts ergibt ein Bytes-Objekt. Das Slicing eines Bytearray-Objekts ergibt ein Bytearray-Objekt.

Slice-Zuweisungen an ein Bytearray-Objekt akzeptieren alles, was die PEP 3118 Buffer-API implementiert, oder ein Iterable von ganzen Zahlen im Bereich von 0 bis 255.

Indizierung

Die Indizierung von Bytes und Bytearray gibt kleine ganze Zahlen zurück (wie der Bytes-Typ in 3.0a1 und wie Listen oder array.array('B')).

Die Zuweisung an ein Element eines Bytearray-Objekts akzeptiert eine ganze Zahl im Bereich von 0 bis 255. (Um von einer Byte-Sequenz zuzuweisen, verwenden Sie eine Slice-Zuweisung.)

Str() und Repr()

Die Funktionen str() und repr() geben dasselbe für diese Objekte zurück. repr() eines Bytes-Objekts gibt ein Literal im b'...'-Stil zurück. repr() eines Bytearrays gibt einen String der Form "bytearray(b'...')" zurück.

Operatoren

Die folgenden Operatoren werden von den Bytes- und Bytearray-Typen implementiert, sofern nicht anders angegeben

  • b1 + b2: Verkettung. Bei gemischten Bytes/Bytearray-Operanden ist der Rückgabetyp der des ersten Arguments (dies scheint willkürlich zu sein, bis man betrachtet, wie += funktioniert).
  • b1 += b2: modifiziert b1, wenn es sich um ein Bytearray-Objekt handelt.
  • b * n, n * b: Wiederholung; n muss eine ganze Zahl sein.
  • b *= n: modifiziert b, wenn es sich um ein Bytearray-Objekt handelt.
  • b1 in b2, b1 not in b2: Substring-Test; b1 kann jedes Objekt sein, das die PEP 3118 Buffer-API implementiert.
  • i in b, i not in b: Test auf Mitgliedschaft eines einzelnen Bytes; i muss eine ganze Zahl sein (wenn es ein Byte-Array der Länge 1 ist, wird es als Substring-Test betrachtet, mit demselben Ergebnis).
  • len(b): die Anzahl der Bytes.
  • hash(b): der Hash-Wert; nur implementiert vom Bytes-Typ.

Beachten Sie, dass der %-Operator *nicht* implementiert ist. Es scheint nicht lohnenswert, die Komplexität dafür in Kauf zu nehmen.

Methoden

Die folgenden Methoden werden sowohl von Bytes als auch von Bytearray mit ähnlicher Semantik implementiert. Sie akzeptieren alles, was die PEP 3118 Buffer-API für Byte-Argumente implementiert und geben denselben Typ zurück wie das Objekt, dessen Methode aufgerufen wird ("self")

.capitalize(), .center(), .count(), .decode(), .endswith(),
.expandtabs(), .find(), .index(), .isalnum(), .isalpha(), .isdigit(),
.islower(), .isspace(), .istitle(), .isupper(), .join(), .ljust(),
.lower(), .lstrip(), .partition(), .replace(), .rfind(), .rindex(),
.rjust(), .rpartition(), .rsplit(), .rstrip(), .split(),
.splitlines(), .startswith(), .strip(), .swapcase(), .title(),
.translate(), .upper(), .zfill()

Dies ist genau die Menge der Methoden, die im str-Typ in Python 2.x vorhanden sind, mit Ausnahme von .encode(). Die Signaturen und die Semantik sind ebenfalls gleich. Immer wenn jedoch Zeichenklassen wie Buchstabe, Leerzeichen, Kleinbuchstabe verwendet werden, werden die ASCII-Definitionen dieser Klassen verwendet. (Der str-Typ von Python 2.x verwendet die Definitionen aus dem aktuellen Locale, einstellbar über das locale-Modul.) Die Methode .encode() wird aufgrund der strengeren Definitionen von Kodierung und Dekodierung in Python 3000 weggelassen: Kodierung nimmt immer einen Unicode-String und gibt eine Byte-Sequenz zurück, und Dekodierung nimmt immer eine Byte-Sequenz und gibt einen Unicode-String zurück.

Zusätzlich implementieren beide Typen die Klassenmethode .fromhex(), die ein Objekt aus einem String erstellt, der Hexadezimalwerte enthält (mit oder ohne Leerzeichen zwischen den Bytes).

Der Bytearray-Typ implementiert diese zusätzlichen Methoden aus dem MutableSequence ABC (siehe PEP 3119)

.extend(), .insert(), .append(), .reverse(), .pop(), .remove().

Bytes und der Str-Typ

Ähnlich wie der Bytes-Typ in Python 3.0a1 und im Gegensatz zur Beziehung zwischen str und unicode in Python 2.x lösen Versuche, Bytes- (oder Bytearray-)Objekte und String-Objekte zu mischen, ohne eine Kodierung anzugeben, eine TypeError-Ausnahme aus. (Das Vergleichen von Bytes/Bytearray und String-Objekten auf Gleichheit ergibt jedoch einfach False; siehe den Abschnitt über Vergleiche oben.)

Konvertierungen zwischen Bytes- oder Bytearray-Objekten und String-Objekten müssen immer explizit unter Verwendung einer Kodierung erfolgen. Es gibt zwei äquivalente APIs: str(b, <encoding>[, <errors>]) ist äquivalent zu b.decode(<encoding>[, <errors>]) und bytes(s, <encoding>[, <errors>]) ist äquivalent zu s.encode(<encoding>[, <errors>]).

Es gibt eine Ausnahme: Wir können von Bytes (oder Bytearray) zu str konvertieren, ohne eine Kodierung anzugeben, indem wir str(b) schreiben. Dies erzeugt dasselbe Ergebnis wie repr(b). Diese Ausnahme ist notwendig aufgrund des allgemeinen Versprechens, dass *jedes* Objekt gedruckt werden kann, und Drucken ist nur ein Spezialfall der Konvertierung in str. Es gibt jedoch keine Garantie, dass das Drucken eines Bytes-Objekts die einzelnen Bytes als Zeichen interpretiert (im Gegensatz zu Python 2.x).

Der str-Typ implementiert derzeit die PEP 3118 Buffer-API. Dies ist zwar vielleicht gelegentlich praktisch, aber auch potenziell verwirrend, da die über die Buffer-API zugänglichen Bytes eine plattformabhängige Kodierung darstellen: je nach Plattform-Byte-Reihenfolge und einer Compile-Zeit-Konfigurationsoption kann die Kodierung UTF-16-BE, UTF-16-LE, UTF-32-BE oder UTF-32-LE sein. Schlimmer noch, eine andere Implementierung des str-Typs könnte die Byte-Darstellung vollständig ändern, z. B. in UTF-8, oder es sogar unmöglich machen, auf die Daten als zusammenhängendes Byte-Array zuzugreifen. Daher wird die PEP 3118 Buffer-API aus dem str-Typ entfernt.

Der Typ basestring

Der Typ basestring wird aus der Sprache entfernt. Code, der früher isinstance(x, basestring) lautete, sollte geändert werden, um stattdessen isinstance(x, str) zu verwenden.

Pickling

Bleibt als Übung für den Leser.


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

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