Array -Objekt kann viele konkrete Formen annehmen. Es kann sich um ein eindimensionales (1D) -Array von Booleschen oder ein dreidimensionales (3D) -Array von nicht signierten Ganzzahlen von 8-Bit-Zahlen handeln. Als integrierte Funktion isinstance() wird zeigen, jedes Array ist eine Instanz von np.ndarrayunabhängig von der Kind oder der Artwork der im Array gespeicherten Elemente, dh der dtype. In ähnlicher Weise spezifizieren viele Typen-Anbieter-Schnittstellen noch nur noch np.ndarray:

import numpy as np

def course of(
    x: np.ndarray,
    y: np.ndarray,
    ) -> np.ndarray: ...

Solche Typanmerkungen sind nicht ausreichend: Die meisten Schnittstellen haben starke Erwartungen an die Kind oder dtype von bestandenen Arrays. Der meiste Code fällt fehl, wenn ein 3D -Array übergeben wird, an dem ein 1D -Array erwartet wird oder ein Array von Daten übergeben wird, an dem ein Array von Schwimmern erwartet wird.

Den Generika voll ausnutzen np.ndarrayArray -Kind und dtype Eigenschaften können jetzt vollständig angegeben werden:

def course of(
    x: np.ndarray(tuple(int), np.dtype(np.bool_)),
    y: np.ndarray(tuple(int, int, int), np.dtype(np.uint8)),
    ) -> np.ndarray(tuple(int), np.dtype(np.float64)): ...

Mit solchen Particulars mögen jüngste Versionen statischer Analyse -Instruments nach wie vor mypy Und pyright kann Probleme finden, bevor Code überhaupt ausgeführt wird. Darüber hinaus sind die auf Numpy spezialisierten Laufzeit-Validatoren wie Staticframe‚S sf.CallGuardkann die gleichen Anmerkungen zur Laufzeitvalidierung wiederverwenden.

Generische Typen in Python

Generische integrierte Behälter wie z. checklist Und dict Kann durch Angabe für jede Schnittstelle die enthaltenen Typen konkret gemacht werden. Eine Funktion kann erklären, dass es a dauert checklist von str mit checklist(str); oder a dict von str Zu bool kann angegeben werden mit dict(str, bool).

Das Generika np.ndarray

Ein np.ndarray ist ein n-dimensionales Array eines einzelnen Elementtyps (oder dtype). Der np.ndarray generic nimmt zwei Typparameter ein: die erste definiert die Kind mit a tupleDie zweite definiert den Elementtyp mit dem Generikum np.dtype. Während np.ndarray hat zwei Typparameter seit einiger Zeit genommen, die Definition des ersten Parameters, Kind, erst in Numpy 2.1 angegeben.

Der Formypparameter

Beim Erstellen eines Arrays mit Schnittstellen wie np.empty oder np.fullEin Formargument wird als Tupel angegeben. Die Länge des Tupels definiert die Dimensionalität des Arrays; Die Größe jeder Place definiert die Größe dieser Dimension. So eine Kind (10,) ist ein 1D -Array von 10 Elementen; eine Kind (10, 100, 1000) ist ein dreidimensionales Array der Größe 10 um 100 mal 1000.

Bei Verwendung a tuple Kind in der definieren np.ndarray Generisches, derzeit kann nur die Anzahl der Abmessungen für die Typprüfung verwendet werden. So a tuple(int) kann ein 1D -Array angeben; A tuple(int, int, int) kann ein 3D -Array angeben; A tuple(int, ...)Angabe eines Tupels mit null oder mehr Ganzzahlen bedeutet ein n-dimensionales Array. Es könnte in Zukunft möglich sein, einen zu überprüfen und ein np.ndarray mit spezifischen Größen professional Dimension (Verwendung Literal), aber das wird noch nicht allgemein unterstützt.

Der dtype Geben Sie Parameter ein

Der Numpy dtype Objekt definiert Elementtypen und für einige Typen andere Merkmale wie Größe (für Unicode- und String -Typen) oder Einheit (für np.datetime64 Typen). Der dtype selbst ist generisch und nimmt einen numpy „generischen“ Typ als Typparameter. Die schmalsten Typen geben beispielsweise bestimmte Elementmerkmale an np.uint8Anwesend np.float64oder np.bool_. Über diese engen Typen hinaus liefert Numpy allgemeinere Typen, wie z. np.integerAnwesend np.inexactoder np.quantity.

Herstellung np.ndarray Beton

Die folgenden Beispiele veranschaulichen Beton np.ndarray Definitionen:

Eine 1D -Reihe von Booleschen:

np.ndarray(tuple(int), np.dtype(np.bool_))

Ein 3D-Anteil nicht signierter 8-Bit-Ganzzahlen:

np.ndarray(tuple(int, int, int), np.dtype(np.uint8))

Ein zweidimensionales (2D) -Array von Unicode-Zeichenfolgen:

np.ndarray(tuple(int, int), np.dtype(np.str_))

Ein 1D -Array eines numerischen Typs:

np.ndarray(tuple(int), np.dtype(np.quantity))

Statische Typ Überprüfung mit MyPy

Einmal der generische np.ndarray wird konkret gemacht, mypy oder ähnliche Typ -Checkers können für einige Codepfade Werte identifizieren, die mit einer Schnittstelle nicht kompatibel sind.

Beispielsweise erfordert die folgende Funktion ein 1D -Array von signierten Ganzzahlen. Wie unten gezeigt, scheitern vor nicht signierte Ganzzahlen oder Dimensionalitäten als eins mypy Überprüfungen.

def process1(x: np.ndarray(tuple(int), np.dtype(np.signedinteger))): ...

a1 = np.empty(100, dtype=np.int16)
process1(a1) # mypy passes

a2 = np.empty(100, dtype=np.uint8)
process1(a2) # mypy fails
# error: Argument 1 to "process1" has incompatible sort
# "ndarray(tuple(int), dtype(unsignedinteger(_8Bit)))";
# anticipated "ndarray(tuple(int), dtype(signedinteger(Any)))"  (arg-type)

a3 = np.empty((100, 100, 100), dtype=np.int64)
process1(a3) # mypy fails
# error: Argument 1 to "process1" has incompatible sort
# "ndarray(tuple(int, int, int), dtype(signedinteger(_64Bit)))";
# anticipated "ndarray(tuple(int), dtype(signedinteger(Any)))"

Laufzeitvalidierung mit sf.CallGuard

Nicht alle Array -Operationen können die Kind statisch definieren oder dtype einer resultierenden Array. Aus diesem Grund wird die statische Analyse nicht alle nicht übereinstimmenden Schnittstellen fangen. Typ-Anmerkungen sind besser als redundante Validierungscode für viele Funktionen zu erstellen, und können für die Laufzeitvalidierung mit Instruments wiederverwendet werden, die für Numpy-Typen spezialisiert sind.

Der Staticframe CallGuard Die Schnittstelle bietet zwei Dekorateure, test Und warndie Ausnahmen bzw. Warnungen zu Validierungsfehlern erhöhen. Diese Dekorateure validieren Typ-Annotationen gegen die Eigenschaften von Laufzeitobjekten.

Zum Beispiel durch Hinzufügen sf.CallGuard.test In der folgenden Funktion fehlen die Arrays die Validierung mit ausdrucksstark CallGuard Ausnahmen:

import static_frame as sf

@sf.CallGuard.test
def process2(x: np.ndarray(tuple(int), np.dtype(np.signedinteger))): ...

b1 = np.empty(100, dtype=np.uint8)
process2(b1)
# static_frame.core.type_clinic.ClinicError:
# In args of (x: ndarray(tuple(int), dtype(signedinteger))) -> Any
# └── In arg x
#     └── ndarray(tuple(int), dtype(signedinteger))
#         └── dtype(signedinteger)
#             └── Anticipated signedinteger, supplied uint8 invalid

b2 = np.empty((10, 100), dtype=np.int8)
process2(b2)
# static_frame.core.type_clinic.ClinicError:
# In args of (x: ndarray(tuple(int), dtype(signedinteger))) -> Any
# └── In arg x
#     └── ndarray(tuple(int), dtype(signedinteger))
#         └── tuple(int)
#             └── Anticipated tuple size of 1, supplied tuple size of two

Abschluss

Es können mehr durchgeführt werden, um die Numpy -Typisierung zu verbessern. Zum Beispiel die np.object_ Typ könnte generisch gemacht werden, so dass Python In einem Objektarray enthaltenen Typen können definiert werden. Zum Beispiel könnte eine 1D -Objektarray von Zahlenpaaren mit Annotation als:

np.ndarray(tuple(int), np.dtype(np.object_(tuple(int, int))))

Ferner Einheiten von np.datetime64 kann noch nicht statisch angegeben werden. Zum Beispiel könnten Datumseinheiten von Nanosekundeneinheiten mit Anmerkungen wie unterschieden werden np.dtype(np.datetime64(Literal('D'))) oder np.dtype(np.datetime64(Literal('ns'))).

Selbst mit Einschränkungen fangen voll spezifizierte Anmerkungen vom Typ Numpy-Typ Fehler auf und verbessern die Codequalität. Wie gezeigt, Statische Analyse kann eine nicht übereinstimmende Kind identifizieren oder dtypeund Validierung mit sf.CallGuard kann starke Laufzeitgarantien liefern.

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert