Wie kann ich in Python 3 überprüfen, ob ein Objekt ein Container ist (und nicht ein Iterator, der nur einen Durchgang erlaubt)?
Hier ist ein Beispiel:
%Vor% Offensichtlich funktioniert die Funktion renormalize
, wenn sie einen Generatorausdruck empfängt, nicht wie vorgesehen. Es nimmt an, dass es mehrmals durch den Container iterieren kann, während der Generator nur einen Durchlauf erlaubt.
Idealerweise würde ich das gerne tun:
%Vor% Wie kann ich is_container
implementieren?
Ich nehme an, ich könnte überprüfen, ob das Argument leer ist, gerade als wir damit beginnen, den zweiten Durchlauf zu machen. Dieser Ansatz funktioniert jedoch nicht für kompliziertere Funktionen, bei denen es nicht offensichtlich ist, wann genau der zweite Durchlauf beginnt. Außerdem würde ich die Validierung lieber am Eingang der Funktion vornehmen, als tief in der Funktion (und umherschalten, wenn die Funktion geändert wird).
Ich kann natürlich die Funktion renormalize
so umschreiben, dass sie mit einem One-Pass-Iterator korrekt funktioniert. Dazu müssen die Eingabedaten in einen Container kopiert werden. Der Leistungseinfluss beim Kopieren von Millionen großer Listen "nur für den Fall, dass sie keine Listen sind" ist lächerlich.
EDIT: Mein ursprüngliches Beispiel verwendet eine weighted_average
Funktion:
Aber es war nicht das beste Beispiel, da die Version von weighted_average
, die neu geschrieben wurde, um einen einzelnen Durchlauf zu verwenden, ohnehin besser ist:
Obwohl alle iterables Collections unterlassen sollten.Ich verhandelbar, leider nicht alle. Hier ist eine Antwort basierend darauf, welche Schnittstelle die Objekte implementieren, anstatt was sie deklarieren.
Kurze Antwort:
Ein "Container", wie Sie ihn nennen, dh eine Liste / ein Tupel, die mehr als einmal iteriert werden kann, anstatt ein Generator zu sein, der erschöpft sein wird, implementiert normalerweise __iter__
und __getitem__
. Daher können Sie dies tun:
Lange Antwort:
Sie können jedoch ein iterables erstellen, das nicht ausgeschöpft wird und getitem nicht unterstützt. Zum Beispiel eine Funktion, die Primzahlen erzeugt. Sie könnten die Generierung oft wiederholen, wenn Sie möchten, aber eine Funktion zum Abrufen der 1065. Primzahl zu haben, würde eine Menge Berechnung erfordern, also möchten Sie das vielleicht nicht unterstützen. : -)
Gibt es also einen "zuverlässigeren" Weg?
Nun, alle iterables implementieren eine Funktion __iter__
, die einen Iterator zurückgibt. Die Iteratoren haben eine __next__
-Funktion. Dies wird beim Iterieren verwendet. Der Aufruf von __next__
wiederholt am Ende den Iterator.
Wenn es also eine Funktion __next__
hat, ist es ein Iterator und wird erschöpft sein.
Iterables, die noch keine Iteratoren sind, haben keine __next__
-Funktion, sondern implementieren eine __iter__
-Funktion, die ein iterables zurückgibt:
So können Sie überprüfen, ob das Objekt __iter__
hat, aber nicht __next__
.
Iterators hat auch eine Funktion __iter__
, die self zurückgibt.
Daher können Sie diese Varianten der Überprüfung tun:
%Vor%Dies würde fehlschlagen, wenn Sie ein Objekt implementieren, das einen zerbrochenen Iterator zurückgibt, eines, das nicht selbst zurückgibt, wenn Sie iter () erneut aufrufen. Aber dann macht dein Code (oder der Code eines Drittanbieters) tatsächlich etwas falsch.
Es hängt jedoch davon ab, einen Iterator zu erstellen und somit die Objekte __iter__
aufzurufen, was theoretisch Nebenwirkungen haben kann, während die obigen hasattr-Aufrufe keine Nebenwirkungen haben sollten. OK, also ruft getattribute auf. Aber Sie können das so beheben:
Dieser ist einigermaßen sicher und sollte in allen Fällen funktionieren, außer wenn das Objekt __next__
oder __iter__
dynamisch auf __getattribute__
Aufrufe generiert, aber wenn Sie das tun, sind Sie verrückt. : -)
Instinktiv wäre meine bevorzugte Version iter(o) is o
, aber ich musste das nie tun, also basiert das nicht auf Erfahrung.
Sie können die im Modul collections
definierten abstrakten Basisklassen verwenden, um zu überprüfen, ob it
eine Instanz von collections.Iterator ist.
Persönlich finde ich jedoch, dass Ihre iteratorfreundliche Version des gewichteten Durchschnitts viel einfacher zu lesen ist als die Multiple List Comprehension / Sum-Version. : -)
Tags und Links python design python-3.x types iterator