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

Python Enhancement Proposals

PEP 209 – Mehrdimensionale Arrays

Autor:
Paul Barrett <barrett at stsci.edu>, Travis Oliphant <oliphant at ee.byu.edu>
Status:
Zurückgezogen
Typ:
Standards Track
Erstellt:
03-Jan-2001
Python-Version:
2.2
Post-History:


Inhaltsverzeichnis

Wichtig

Diese PEP wurde zurückgezogen.

×

Zusammenfassung

Dieses PEP schlägt ein Redesign und eine Neuimplementierung des Moduls für mehrdimensionale Arrays, Numeric, vor, um neue Funktionen und Funktionalitäten einfacher hinzufügen zu können. Aspekte von Numeric 2, denen besondere Aufmerksamkeit gewidmet wird, sind der effiziente Zugriff auf Arrays, die eine Größe von einem Gigabyte überschreiten und aus inhomogenen Datenstrukturen oder Datensätzen bestehen. Das vorgeschlagene Design verwendet vier Python-Klassen: ArrayType, UFunc, Array und ArrayView; und ein Low-Level C-Erweiterungsmodul, _ufunc, um die Array-Operationen effizient zu handhaben. Darüber hinaus hat jeder Array-Typ ein eigenes C-Erweiterungsmodul, das die Regeln für Typumwandlung, Operationen und Methoden für diesen Typ definiert. Dieses Design ermöglicht die modulare Hinzufügung neuer Typen, Funktionen und Funktionalitäten. Die neue Version wird einige Inkompatibilitäten mit dem aktuellen Numeric einführen.

Motivation

Mehrdimensionale Arrays werden häufig zur Speicherung und Manipulation von Daten in Wissenschaft, Ingenieurwesen und Informatik verwendet. Python hat derzeit ein Erweiterungsmodul namens Numeric (fortan Numeric 1 genannt), das eine zufriedenstellende Funktionalität für Benutzer bietet, die homogene Arrays von Daten moderater Größe (ca. 10 MB) manipulieren. Für den Zugriff auf größere Arrays (ca. 100 MB oder mehr) mit möglicherweise inhomogenen Daten ist die Implementierung von Numeric 1 ineffizient und umständlich. Zukünftige Anfragen der Numerical Python-Community nach zusätzlicher Funktionalität sind ebenfalls wahrscheinlich, wie die PEPs 211: Hinzufügen neuer linearer Operatoren zu Python und 225: Elementweise/objektweise Operatoren zeigen.

Vorschlag

Dieser Vorschlag empfiehlt ein Redesign und eine Neuimplementierung von Numeric 1, fortan Numeric 2 genannt, die es ermöglichen wird, neue Typen, Funktionen und Funktionalitäten einfach und modular hinzuzufügen. Das anfängliche Design von Numeric 2 sollte sich auf die Bereitstellung eines generischen Frameworks für die Manipulation von Arrays verschiedener Typen konzentrieren und einen unkomplizierten Mechanismus für das Hinzufügen neuer Array-Typen und UFuncs ermöglichen. Funktionsmethoden, die spezifischer für verschiedene Disziplinen sind, können dann auf diesem Kern aufbauen. Dieses neue Modul wird weiterhin Numeric heißen und das meiste Verhalten von Numeric 1 wird beibehalten.

Das vorgeschlagene Design verwendet vier Python-Klassen: ArrayType, UFunc, Array und ArrayView; und ein Low-Level C-Erweiterungsmodul, um die Array-Operationen effizient zu handhaben. Darüber hinaus hat jeder Array-Typ ein eigenes C-Erweiterungsmodul, das die Regeln für Typumwandlung, Operationen und Methoden für diesen Typ definiert. Zu einem späteren Zeitpunkt, wenn die Kernfunktionalität stabil ist, können einige Python-Klassen in C-Erweiterungstypen umgewandelt werden.

Einige geplante Funktionen sind

  1. Verbesserte Speichernutzung

    Diese Funktion ist besonders wichtig bei der Handhabung großer Arrays und kann zu erheblichen Verbesserungen bei der Leistung und der Speichernutzung führen. Wir haben mehrere Bereiche identifiziert, in denen die Speichernutzung verbessert werden kann

    1. Verwenden eines lokalen Typumwandlungsmodells

      Anstatt Pythons globales Typumwandlungsmodell zu verwenden, das temporäre Arrays erzeugt, wird Numeric 2, wie Numeric 1, ein lokales Typumwandlungsmodell gemäß PEP 208 implementieren, das die Verantwortung für die Typumwandlung auf den Operator verlagert. Durch die Verwendung interner Puffer kann eine Typumwandlungsoperation für jedes Array (einschließlich Ausgabearrays) bei Bedarf zum Zeitpunkt der Operation durchgeführt werden. Benchmarks [1] haben gezeigt, dass die Leistung höchstens leicht beeinträchtigt wird und sich in Fällen, in denen die internen Puffer kleiner als die L2-Cache-Größe sind und der Prozessor unter Last steht, verbessert. Um eine Array-Typumwandlung ganz zu vermeiden, sind C-Funktionen mit Argumenten gemischten Typs in Numeric 2 zulässig.

    2. Vermeiden der Erzeugung temporärer Arrays

      In komplexen Array-Ausdrücken (d.h. mit mehr als einer Operation) erzeugt jede Operation ein temporäres Array, das vom nachfolgenden Vorgang verwendet und dann gelöscht wird. Ein besserer Ansatz wäre es, diese temporären Arrays zu identifizieren und ihre Datenpuffer wiederzuverwenden, wenn möglich, d.h. wenn die Array-Form und der Typ mit dem zu erzeugenden temporären Array übereinstimmen. Dies kann durch Überprüfung der Referenzzählung des temporären Arrays erfolgen. Wenn diese 1 ist, wird es gelöscht, sobald die Operation abgeschlossen ist, und ist ein Kandidat für die Wiederverwendung.

    3. Optionale Verwendung von speicherabbildungsdateien

      Numeric-Benutzer müssen manchmal auf Daten aus sehr großen Dateien zugreifen oder Daten verarbeiten, die größer als der verfügbare Speicher sind. Speicherabbildungsarrays bieten einen Mechanismus dafür, indem sie die Daten auf der Festplatte speichern, während sie so erscheinen, als wären sie im Speicher. Speicherabbildungsarrays sollten den Zugriff auf alle Dateien verbessern, indem sie einen von zwei Kopiervorgängen während eines Dateizugriffs eliminieren. Numeric sollte transparent auf In-Memory- und Speicherabbildungsarrays zugreifen können.

    4. Zugriff auf Datensätze

      In einigen Wissenschaftsbereichen werden Daten in Dateien als binäre Datensätze gespeichert. Beispielsweise werden in der Astronomie Photonen als eindimensionale Liste von Photonen in der Reihenfolge ihrer Ankunftszeit gespeichert. Diese Datensätze oder C-ähnlichen Strukturen enthalten Informationen über das erfasste Photon, wie z. B. seine Ankunftszeit, seine Position auf dem Detektor und seine Energie. Jedes Feld kann einen anderen Typ haben, wie z. B. char, int oder float. Solche Arrays führen neue Probleme mit sich, die behandelt werden müssen, insbesondere Byte-Ausrichtung oder Byte-Swapping können erforderlich sein, damit die numerischen Werte ordnungsgemäß zugegriffen werden können (obwohl Byte-Swapping auch ein Problem bei speicherabbildungsdaten ist). Numeric 2 ist so konzipiert, dass Ausrichtungs- und Darstellungsprobleme beim Zugriff auf oder bei der Operation mit Daten automatisch behandelt werden. Es gibt zwei Ansätze zur Implementierung von Datensätzen: als abgeleitete Array-Klasse oder als spezieller Array-Typ, je nach Sichtweise. Wir vertagen diese Diskussion auf den Abschnitt „Offene Fragen“.

  2. Zusätzliche Array-Typen

    Numeric 1 hat 11 definierte Typen: char, ubyte, sbyte, short, int, long, float, double, cfloat, cdouble und object. Es gibt keine ushort-, uint- oder ulong-Typen, noch gibt es komplexere Typen wie einen Bit-Typ, der für einige Wissenschaftsbereiche und möglicherweise für die Implementierung von maskierten Arrays nützlich ist. Das Design von Numeric 1 macht die Hinzufügung dieser und anderer Typen zu einem schwierigen und fehleranfälligen Prozess. Um die einfache Hinzufügung (und Löschung) neuer Array-Typen wie des unten beschriebenen Bit-Typs zu ermöglichen, ist ein Redesign von Numeric erforderlich.

    1. Bit-Typ

      Das Ergebnis eines Rich-Vergleichs zwischen Arrays ist ein Array von booleschen Werten. Das Ergebnis kann in einem Array vom Typ char gespeichert werden, was aber eine unnötige Speicherverschwendung darstellt. Eine bessere Implementierung würde einen Bit- oder Booleschen Typ verwenden, der die Array-Größe um den Faktor acht komprimiert. Dies wird derzeit für Numeric 1 implementiert (von Travis Oliphant) und sollte in Numeric 2 enthalten sein.

  3. Erweiterte Array-Indexierungs-Syntax

    Die erweiterte Slice-Syntax wurde zu Python hinzugefügt, um mehr Flexibilität bei der Manipulation von Numeric-Arrays durch die Zulassung von Schrittgrößen größer als 1 zu bieten. Diese Syntax funktioniert gut als Kurzform für eine Liste von regelmäßig beabstandeten Indizes. Für Situationen, in denen eine Liste von unregelmäßig beabstandeten Indizes benötigt wird, würde eine erweiterte Array-Indexierungs-Syntax die Verwendung von 1-D-Arrays als Argumente ermöglichen.

  4. Reiche Vergleiche

    Die Implementierung von PEP 207: Rich Comparisons in Python 2.1 bietet zusätzliche Flexibilität bei der Manipulation von Arrays. Wir beabsichtigen, diese Funktion in Numeric 2 zu implementieren.

  5. Array-Broadcasting-Regeln

    Wenn eine Operation zwischen einem Skalar und einem Array durchgeführt wird, ist das implizite Verhalten die Erzeugung eines neuen Arrays mit der gleichen Form wie der Array-Operand, das den Skalarwert enthält. Dies wird als Array-Broadcasting bezeichnet. Es funktioniert auch mit Arrays geringeren Ranges, wie z. B. Vektoren. Dieses implizite Verhalten ist in Numeric 1 implementiert und wird auch in Numeric 2 implementiert.

Design und Implementierung

Das Design von Numeric 2 hat vier Hauptklassen

  1. ArrayType

    Dies ist eine einfache Klasse, die die grundlegenden Eigenschaften eines Array-Typs beschreibt, z. B. seinen Namen, seine Größe in Bytes, seine Typumwandlungsrelationen in Bezug auf andere Typen usw., z. B.

    Int32 = ArrayType('Int32', 4, 'doc-string')
    

    Seine Beziehung zu den anderen Typen wird definiert, wenn das C-Erweiterungsmodul für diesen Typ importiert wird. Der entsprechende Python-Code ist

    Int32.astype[Real64] = Real64
    

    Dies besagt, dass der Real64-Array-Typ eine höhere Priorität hat als der Int32-Array-Typ.

    Die folgenden Attribute und Methoden werden für die Kernimplementierung vorgeschlagen. Zusätzliche Attribute können individuell hinzugefügt werden, z. B. .bitsize oder .bitstrides für den Bit-Typ.

    Attribute

    .name:                  e.g. "Int32", "Float64", etc.
    .typecode:              e.g. 'i', 'f', etc.
                            (for backward compatibility)
    .size (in bytes):       e.g. 4, 8, etc.
    .array_rules (mapping): rules between array types
    .pyobj_rules (mapping): rules between array and python types
    .doc:                   documentation string
    

    Methoden

    __init__():             initialization
    __del__():              destruction
    __repr__():             representation
    

    C-API: Dies muss noch ausgearbeitet werden.

  2. UFunc

    Diese Klasse ist das Herzstück von Numeric 2. Ihr Design ähnelt dem von ArrayType, insofern als UFunc ein singuläres aufrufbares Objekt erzeugt, dessen Attribute Name, Gesamtanzahl und Anzahl der Eingabeargumente, ein Dokumentationsstring und ein leeres CFunc-Dictionary sind; z. B.

    add = UFunc('add', 3, 2, 'doc-string')
    

    Wenn definiert, hat die Instanz add keine zugehörigen C-Funktionen und kann daher keine Arbeit verrichten. Das CFunc-Dictionary wird später gefüllt oder registriert, wenn das C-Erweiterungsmodul eines Array-Typs importiert wird. Die Argumente der Register-Methode sind: Funktionsname, Funktionsdeskriptor und das CUFunc-Objekt. Der entsprechende Python-Code ist

    add.register('add', (Int32, Int32, Int32), cfunc-add)
    

    In der Initialisierungsfunktion eines Array-Typ-Moduls, z. B. Int32, gibt es zwei C-API-Funktionen: eine zur Initialisierung der Typumwandlungsregeln und die andere zur Registrierung der CFunc-Objekte.

    Wenn eine Operation auf einige Arrays angewendet wird, wird die Methode __call__ aufgerufen. Sie ruft den Typ jedes Arrays ab (wenn das Ausgabearray nicht angegeben ist, wird es aus den Typumwandlungsregeln erstellt) und prüft das CFunc-Dictionary auf einen Schlüssel, der den Argumenttypen entspricht. Wenn dieser existiert, wird die Operation sofort ausgeführt, andernfalls werden die Typumwandlungsregeln verwendet, um nach einer verwandten Operation und einem Satz von Konvertierungsfunktionen zu suchen. Die Methode __call__ ruft dann eine in C geschriebene Compute-Methode auf, um über die Slices jedes Arrays zu iterieren, nämlich

    _ufunc.compute(slice, data, func, swap, conv)
    

    Das Argument ‚func‘ ist ein CFuncObject, während die Argumente ‚swap‘ und ‚conv‘ Listen von CFuncObjects für die Arrays sind, die eine Vor- oder Nachverarbeitung benötigen, andernfalls wird None verwendet. Das Datenargument ist eine Liste von Pufferobjekten, und das Slice-Argument gibt die Anzahl der Iterationen für jede Dimension zusammen mit dem Pufferoffset und der Schrittgröße für jedes Array und jede Dimension an.

    Wir haben mehrere UFuncs für die Verwendung durch die Methode __call__ vordefiniert: cast, swap, getobj und setobj. Die cast- und swap-Funktionen führen Typumwandlung und Byte-Swapping durch, und die getobj- und setobj-Funktionen führen Typumwandlung zwischen Numeric-Arrays und Python-Sequenzen durch.

    Die folgenden Attribute und Methoden werden für die Kernimplementierung vorgeschlagen.

    Attribute

    .name:                  e.g. "add", "subtract", etc.
    .nargs:                 number of total arguments
    .iargs:                 number of input arguments
    .cfuncs (mapping):      the set C functions
    .doc:                   documentation string
    

    Methoden

    __init__():             initialization
    __del__():              destruction
    __repr__():             representation
    __call__():             look-up and dispatch method
    initrule():             initialize coercion rule
    uninitrule():           uninitialize coercion rule
    register():             register a CUFunc
    unregister():           unregister a CUFunc
    

    C-API: Dies muss noch ausgearbeitet werden.

  3. Array

    Diese Klasse enthält Informationen über das Array, wie Form, Typ, Endianness der Daten usw. Ihre Operatoren, '+', '-', etc. rufen lediglich die entsprechende UFunc-Funktion auf, z. B.

    def __add__(self, other):
        return ufunc.add(self, other)
    

    Die folgenden Attribute, Methoden und Funktionen werden für die Kernimplementierung vorgeschlagen.

    Attribute

    .shape:                 shape of the array
    .format:                type of the array
    .real (only complex):   real part of a complex array
    .imag (only complex):   imaginary part of a complex array
    

    Methoden

    __init__():             initialization
    __del__():              destruction
    __repr_():              representation
    __str__():              pretty representation
    __cmp__():              rich comparison
    __len__():
    __getitem__():
    __setitem__():
    __getslice__():
    __setslice__():
    numeric methods:
    copy():                 copy of array
    aslist():               create list from array
    asstring():             create string from array
    

    Funktionen

    fromlist():             create array from sequence
    fromstring():           create array from string
    array():                create array with shape and value
    concat():               concatenate two arrays
    resize():               resize array
    

    C-API: Dies muss noch ausgearbeitet werden.

  4. ArrayView

    Diese Klasse ist ähnlich der Array-Klasse, außer dass die Methoden reshape und flat Ausnahmen auslösen, da nicht-kontinuierliche Arrays nicht nur mit Zeiger- und Schrittgrößeninformationen umgeformt oder abgeflacht werden können.

    C-API: Dies muss noch ausgearbeitet werden.

  5. C-Erweiterungsmodule

    Numeric2 wird mehrere C-Erweiterungsmodule haben.

    1. _ufunc

      Das Hauptmodul dieser Sammlung ist _ufuncmodule.c. Die Absicht dieses Moduls ist es, das absolute Minimum zu tun, d. h. Arrays mit einer angegebenen C-Funktion zu durchlaufen. Die Schnittstelle dieser Funktionen ist dieselbe wie bei Numeric 1, d. h.

      int (*CFunc)(char *data, int *steps, int repeat, void *func);
      

      und ihre Funktionalität soll dieselbe sein, d. h. sie iterieren über die innerste Dimension.

      Die folgenden Attribute und Methoden werden für die Kernimplementierung vorgeschlagen.

      Attribute

      Methoden

      compute():
      

      C-API: Dies muss noch ausgearbeitet werden.

    2. _int32, _real64, etc.

      Es wird auch C-Erweiterungsmodule für jeden Array-Typ geben, z. B. _int32module.c, _real64module.c, usw. Wie bereits erwähnt, registrieren diese Module bei Import durch das UFunc-Modul automatisch ihre Funktionen und Typumwandlungsregeln. Neue oder verbesserte Versionen dieser Module können leicht implementiert und verwendet werden, ohne den Rest von Numeric 2 zu beeinträchtigen.

Offene Fragen

  1. Standardmäßig gibt die Slice-Syntax Kopier- oder View-Verhalten zurück?

    Das Standardverhalten von Python ist die Rückgabe einer Kopie einer Teilliste oder eines Tupels bei Verwendung der Slice-Syntax, während Numeric 1 eine View auf das Array zurückgibt. Die für Numeric 1 getroffene Wahl wurde offenbar aus Leistungsgründen getroffen: Die Entwickler möchten die Kosten für die Allokation und Kopie des Datenpuffers bei jeder Array-Operation vermeiden und sind der Meinung, dass die Notwendigkeit einer tiefen Kopie eines Arrays selten ist. Einige haben jedoch argumentiert, dass die Slice-Notation von Numeric ebenfalls Kopierverhalten haben sollte, um mit Python-Listen konsistent zu sein. In diesem Fall können die mit dem Kopierverhalten verbundenen Leistungseinbußen durch die Implementierung von Copy-on-Write minimiert werden. Dieses Schema weist beide Arrays an, bis zu einem Datenpuffer zu teilen (wie im View-Verhalten), bis eines der Arrays neue Daten zugewiesen bekommt, woraufhin eine Kopie des Datenpuffers erstellt wird. Das View-Verhalten würde dann durch eine ArrayView-Klasse implementiert, deren Verhalten dem von Numeric 1-Arrays ähnelt, d.h. .shape ist für nicht-kontinuierliche Arrays nicht setzbar. Die Verwendung einer ArrayView-Klasse macht auch explizit, welche Art von Daten das Array enthält.

  2. Gibt die Element-Syntax standardmäßig Kopier- oder View-Verhalten zurück?

    Eine ähnliche Frage stellt sich bei der Element-Syntax. Zum Beispiel, wenn a = [[0,1,2], [3,4,5]] und b = a[0], dann ändert die Änderung von b[0] auch a[0][0], weil a[0] eine Referenz oder View auf die erste Zeile von a ist. Daher sollte, wenn c ein 2-D-Array ist, c[i] ein 1-D-Array zurückgeben, das eine View von c ist, anstatt eine Kopie davon, um konsistent zu sein. Dennoch kann c[i] als bloße Kurzform für c[i,:] betrachtet werden, was ein Kopierverhalten implizieren würde, unter der Annahme, dass die Slice-Syntax eine Kopie zurückgibt. Sollte Numeric 2 dasselbe Verhalten wie Listen aufweisen und eine View zurückgeben oder eine Kopie?

  3. Wie wird die Skalar-Typumwandlung implementiert?

    Python hat weniger numerische Typen als Numeric, was zu Typumwandlungsproblemen führen kann. Beispielsweise wird beim Multiplizieren eines Python-Skalars vom Typ float und eines Numeric-Arrays vom Typ float das Numeric-Array in ein Double konvertiert, da der Python-Float-Typ tatsächlich ein Double ist. Dies ist oft nicht das gewünschte Verhalten, da das Numeric-Array in der Größe verdoppelt wird, was wahrscheinlich ärgerlich ist, insbesondere bei sehr großen Arrays. Wir bevorzugen, dass der Array-Typ Vorrang vor dem Python-Typ für die gleiche Typklasse hat, nämlich Ganzzahl, Gleitkommazahl und komplex. Daher gibt eine Operation zwischen einer Python-Ganzzahl und einem Int16 (short) Array ein Int16-Array zurück. Während eine Operation zwischen einem Python-Float und einem Int16-Array ein Float64 (double) Array zurückgeben würde. Operationen zwischen zwei Arrays verwenden normale Typumwandlungsregeln.

  4. Wie wird die Ganzzahl-Division behandelt?

    In einer zukünftigen Version von Python wird sich das Verhalten der Ganzzahl-Division ändern. Die Operanden werden in floats konvertiert, so dass das Ergebnis ein float sein wird. Wenn wir die vorgeschlagenen Skalar-Typumwandlungsregeln implementieren, bei denen Arrays Vorrang vor Python-Skalaren haben, dann gibt das Teilen eines Arrays durch eine Ganzzahl ein Ganzzahl-Array zurück und ist nicht konsistent mit einer zukünftigen Version von Python, die ein Array vom Typ double zurückgeben würde. Wissenschaftliche Programmierer sind mit dem Unterschied zwischen Ganzzahl- und Gleitkomma-Division vertraut, also sollte Numeric 2 dieses Verhalten beibehalten?

  5. Wie sollen Datensätze implementiert werden?

    Es gibt zwei Ansätze zur Implementierung von Datensätzen, je nach Blickwinkel. Der erste besteht darin, Arrays in separate Klassen zu unterteilen, je nach Verhalten ihrer Typen. Zum Beispiel sind numerische Arrays eine Klasse, Strings eine zweite und Datensätze eine dritte, da der Bereich und die Art der Operationen jeder Klasse unterschiedlich sind. Als solches ist ein Datensatz-Array kein neuer Typ, sondern ein Mechanismus für eine flexiblere Array-Form. Um solche komplexen Daten leicht zugänglich und manipulierbar zu machen, besteht die Klasse aus numerischen Arrays mit unterschiedlichen Byte-Offsets im Datenpuffer. Beispielsweise könnte man eine Tabelle haben, die aus einem Array von Int16- und Real32-Werten besteht. Zwei numerische Arrays, eines mit einem Offset von 0 Bytes und einem Stride von 6 Bytes, das als Int16 interpretiert wird, und eines mit einem Offset von 2 Bytes und einem Stride von 6 Bytes, das als Real32 interpretiert wird, würden das Datensatz-Array darstellen. Beide numerischen Arrays würden auf denselben Datenpuffer verweisen, aber unterschiedliche Offset- und Stride-Attribute und einen anderen numerischen Typ haben.

    Der zweite Ansatz ist, einen Datensatz als einen von vielen Array-Typen zu betrachten, wenn auch mit weniger und möglicherweise anderen Array-Operationen als bei numerischen Arrays. Dieser Ansatz betrachtet einen Array-Typ als eine Abbildung eines Strings fester Länge. Die Abbildung kann entweder einfach sein, wie Ganzzahl- und Gleitkommazahlen, oder komplex, wie eine komplexe Zahl, eine Byte-Zeichenkette und eine C-Struktur. Der Datensatztyp verschmilzt effektiv die Module struct und Numeric zu einem mehrdimensionalen Struct-Array. Dieser Ansatz impliziert bestimmte Änderungen an der Array-Schnittstelle. Zum Beispiel sollte das Schlüsselwortargument ‚typecode‘ wahrscheinlich in das beschreibendere Schlüsselwort ‚format‘ geändert werden.

    1. Wie werden Datensatz-Semantiken definiert und implementiert?

      Unabhängig davon, welcher Implementierungsansatz für Datensätze gewählt wird, müssen die Syntax und Semantik des Zugriffs und der Manipulation festgelegt werden, wenn man Zugriff auf Unterfelder von Datensätzen haben möchte. In diesem Fall kann der Datensatztyp im Wesentlichen als inhomogene Liste betrachtet werden, ähnlich einem Tupel, das von der unpack-Methode des struct-Moduls zurückgegeben wird; und ein 1-D-Array von Datensätzen kann als 2-D-Array interpretiert werden, wobei die zweite Dimension der Index in der Feldliste ist. Diese erweiterte Array-Semantik erleichtert den Zugriff auf ein Array eines oder mehrerer Felder auf natürliche und intuitive Weise. Wenn wir davon ausgehen, dass Datensätze als Array-Typ implementiert werden, dann ist die letzte Dimension standardmäßig 0 und kann daher für Arrays mit einfachen Typen wie numerischen vernachlässigt werden.

  6. Wie werden maskierte Arrays implementiert?

    Maskierte Arrays in Numeric 1 werden als separate Array-Klasse implementiert. Mit der Möglichkeit, neue Array-Typen zu Numeric 2 hinzuzufügen, ist es möglich, dass maskierte Arrays in Numeric 2 als neuer Array-Typ anstelle einer Array-Klasse implementiert werden könnten.

  7. Wie werden numerische Fehler behandelt (insbesondere IEEE-Gleitkommafehler)?

    Es ist den Vorschlagenden (Paul Barrett und Travis Oliphant) nicht klar, wie Fehler am besten oder bevorzugt behandelt werden. Da die meisten C-Funktionen, die die Operation ausführen, über die innerste (letzte) Dimension des Arrays iterieren. Diese Dimension kann tausend oder mehr Elemente mit einem oder mehreren Fehlern unterschiedlichen Typs enthalten, wie z. B. Division durch Null, Unterlauf und Überlauf. Darüber hinaus kann die Verfolgung dieser Fehler zu Lasten der Leistung gehen. Daher schlagen wir mehrere Optionen vor

    1. Geben Sie eine Meldung über den schwerwiegendsten Fehler aus und überlassen Sie es dem Benutzer, die Fehler zu lokalisieren.
    2. Geben Sie eine Meldung über alle aufgetretenen Fehler und die Anzahl der Vorkommnisse aus und überlassen Sie es dem Benutzer, die Fehler zu lokalisieren.
    3. Geben Sie eine Meldung über alle aufgetretenen Fehler und eine Liste der Orte aus, an denen sie aufgetreten sind.
    4. Oder verwenden Sie einen hybriden Ansatz, geben Sie nur den schwerwiegendsten Fehler aus, verfolgen Sie aber dennoch, was und wo die Fehler aufgetreten sind. Dies würde es dem Benutzer ermöglichen, die Fehler zu lokalisieren und gleichzeitig die Fehlermeldung kurz zu halten.
  8. Welche Funktionen sind erforderlich, um die Integration von FORTRAN-Bibliotheken und -Code zu erleichtern?

Es wäre ratsam, zu diesem Zeitpunkt zu überlegen, wie die Integration von FORTRAN-Bibliotheken und Benutzercode in Numeric 2 erleichtert werden kann.

Implementierungsschritte

  1. Implementierung grundlegender UFunc-Fähigkeiten
    1. Minimale Array-Klasse

      Notwendige Klassenattribute und -methoden, z. B. .shape, .data, .type, etc.

    2. Minimale ArrayType-Klasse

      Int32, Real64, Complex64, Char, Object

    3. Minimale UFunc-Klasse

      UFunc-Instanziierung, CFunction-Registrierung, UFunc-Aufruf für 1-D-Arrays einschließlich der Regeln für Ausrichtung, Byte-Swapping und Typumwandlung.

    4. Minimales C-Erweiterungsmodul

      _UFunc, das die innerste Array-Schleife in C durchführt.

      Dieser Schritt implementiert alles, was benötigt wird, um: ‚c = add(a, b)‘, wobei a, b und c 1-D-Arrays sind. Er lehrt uns, wie neue UFuncs hinzugefügt werden, wie Arrays typumgewandelt werden, wie die notwendigen Informationen an eine C-Iterator-Methode übergeben werden und wie die eigentliche Berechnung durchgeführt wird.

  2. Fortsetzung der Verbesserung des UFunc-Iterators und der Array-Klasse
    1. Implementierung einiger Zugriffsmethoden für die Array-Klasse: print, repr, getitem, setitem, etc.
    2. Implementierung mehrdimensionaler Arrays
    3. Implementierung einiger grundlegender Array-Methoden mit UFuncs: +, -, *, /, etc.
    4. Ermöglichen Sie UFuncs die Verwendung von Python-Sequenzen.
  3. Vollständige Standard-UFunc- und Array-Klassenfunktionalität
    1. Implementierung des getslice- und setslice-Verhaltens
    2. Arbeit an den Array-Broadcasting-Regeln
    3. Implementierung des Record-Typs
  4. Hinzufügen zusätzlicher Funktionalität
    1. Hinzufügen weiterer UFuncs
    2. Implementierung von Puffer- oder mmap-Zugriff

Inkompatibilitäten

Die folgende Liste enthält Inkompatibilitäten im Verhalten zwischen Numeric 1 und Numeric 2.

  1. Skalar-Typumwandlungsregeln

    Numeric 1 hat einen einzigen Satz von Typumwandlungsregeln für Array- und Python-Numeriktypen. Dies kann zu unerwarteten und ärgerlichen Problemen bei der Berechnung eines Array-Ausdrucks führen. Numeric 2 beabsichtigt, diese Probleme zu überwinden, indem es zwei Sätze von Typumwandlungsregeln gibt: einen für Arrays und Python-Numeriktypen und einen nur für Arrays.

  2. Kein savespace-Attribut

    Das savespace-Attribut in Numeric 1 gibt Arrays mit diesem Attribut Vorrang vor denen, die es nicht gesetzt haben. Numeric 2 wird ein solches Attribut nicht haben und daher werden normale Array-Typumwandlungsregeln gelten.

  3. Slice-Syntax gibt eine Kopie zurück

    Die Slice-Syntax in Numeric 1 gibt eine View auf das ursprüngliche Array zurück. Das Slicing-Verhalten für Numeric 2 wird eine Kopie sein. Sie sollten die ArrayView-Klasse verwenden, um eine View auf ein Array zu erhalten.

  4. Boolesche Vergleiche geben ein boolesches Array zurück

    Ein Vergleich zwischen Arrays in Numeric 1 ergibt einen booleschen Skalar, aufgrund von aktuellen Einschränkungen in Python. Mit dem Aufkommen von Rich Comparisons in Python 2.1 kann ein Array von Booleans zurückgegeben werden.

  5. Typzeichen sind veraltet

    Numeric 2 wird eine ArrayType-Klasse haben, die aus Type-Instanzen besteht, z. B. Int8, Int16, Int32 und Int für vorzeichenbehaftete Ganzzahlen. Das Typecode-Schema in Numeric 1 wird aus Kompatibilitätsgründen verfügbar sein, wird aber als veraltet gelten.

Anhänge

  1. Implizite Iteration über Unter-Arrays

    Eine Computergrafik besteht aus einer Anzahl von 2-D-Bildern oder -Frames gleicher Form. Durch das Stapeln dieser Bilder in einem einzigen Speicherblock wird ein 3-D-Array erstellt. Die auszuführenden Operationen sind jedoch nicht für das gesamte 3-D-Array gedacht, sondern für den Satz von 2-D-Unter-Arrays. In den meisten Array-Sprachen muss jeder Frame extrahiert, bearbeitet und dann unter Verwendung einer For-ähnlichen Schleife wieder in das Ausgabe-Array eingefügt werden. Die J-Sprache erlaubt dem Programmierer, solche Operationen implizit durchzuführen, indem sie einen Rang für den Frame und das Array hat. Standardmäßig sind diese Ränge bei der Erstellung des Arrays gleich. Es war die Absicht der Numeric 1-Entwickler, diese Funktion zu implementieren, da sie auf der Sprache J basiert. Der Numeric 1-Code enthält die erforderlichen Variablen für die Implementierung dieses Verhaltens, wurde aber nie implementiert. Wir beabsichtigen, die implizite Iteration über Unter-Arrays in Numeric 2 zu implementieren, wenn die Array-Broadcasting-Regeln von Numeric 1 dieses Verhalten nicht vollständig unterstützen.

Referenzen


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

Zuletzt geändert: 2024-04-14 13:35:25 GMT