Wie überprüfe ich, ob ein Objekt ein Generatorobjekt in Python ist?

157

Wie überprüfe ich in Python, ob ein Objekt ein Generatorobjekt ist?

Ich versuche das -

>>> type(myobject, generator)

gibt den Fehler -

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'generator' is not defined

(Ich weiß, ich kann überprüfen, ob das Objekt eine hat next Methode hat, um ein Generator zu sein, aber ich möchte eine Möglichkeit, mit der ich den Typ eines Objekts bestimmen kann, nicht nur Generatoren.)

Pushpak Dagade
quelle
4
Welches eigentliche Problem versuchen Sie zu lösen? Wenn Sie mehr Kontext veröffentlichen, gibt es möglicherweise einen intelligenteren Weg. Warum müssen Sie wissen, ob es sich um einen Generator handelt?
Daenyth
7
from types import GeneratorType;type(myobject, GeneratorType)gibt Ihnen das richtige Ergebnis für Objekte der Klasse 'generator'. Aber wie Daenyth andeutet, ist das nicht unbedingt der richtige Weg.
JAB
7
Wenn Sie nachsehen __next__, akzeptieren Sie tatsächlich jeden Iterator, nicht nur Generatoren - was sehr wahrscheinlich das ist, was Sie wollen.
2
So oft wie nicht, ist der eigentliche Punkt, um zu wissen, ob etwas ein Generator ist, in der Lage zu sein, sie zu vermeiden, da dieselbe Sammlung mehrmals durchlaufen werden soll.
Ian
2
Für Leute, die sich über den Anwendungsfall wundern, kann dies nützlich sein, wenn Sie wissen müssen, ob der Iterator verbraucht wird (z. B. wenn Ihre Funktion einen Iterator akzeptiert, aber mehr als einmal iterieren muss, möchten Sie ihn vor dem Iterieren materialisieren)
wbadart

Antworten:

227

Sie können GeneratorType aus folgenden Typen verwenden:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True
utdemir
quelle
5
Dies funktioniert leider nicht für Generatorklassen (z. B. Map- oder Filterobjekte).
Ricardo Cruz
Vielleicht isinstance(gen, (types.GeneratorType, map, filter))ist es auch nützlich, mapund zu erkennen filter. Dies schließt jedoch andere Iterables und Iteratoren nicht ein.
2.
38

Du meinst Generatorfunktionen? verwendeninspect.isgeneratorfunction .

EDIT:

Wenn Sie ein Generatorobjekt möchten, können Sie inspect.isgenerator verwenden, wie von JAB in seinem Kommentar hervorgehoben.

Mouad
quelle
1
Generatorfunktion ist kein Generatorobjekt; siehe @ utdemirs Antwort
Piotr Findeisen
5
@Piotr: In welchem ​​Fall verwenden Sie inspect.isgenerator.
JAB
@JAB, @Piotr: Reflektiert, um alle Möglichkeiten anzusprechen, was das OP bedeuten kann, danke JAB :)
Mouad
1
Hinweis: Wenn Sie nur diesen Test benötigen, können Sie mit der @ utdemir- Lösung einen geringen Overhead vermeiden , da dies inspect.isgeneratornur eine Abkürzung für: ist isinstance(object, types.GeneratorType).
bufh
Siehe @ RobertLujo-Antwort zur Unterscheidung zwischen Generatorobjekt und Generatorfunktion. stackoverflow.com/a/32380774/3595112
Industryworker3595112
24

Ich denke, es ist wichtig, zwischen Generatorfunktionen und Generatoren zu unterscheiden (Ergebnis der Generatorfunktion):

>>> def generator_function():
...     yield 1
...     yield 2
...
>>> import inspect
>>> inspect.isgeneratorfunction(generator_function)
True

Der Aufruf von generator_function liefert kein normales Ergebnis, führt sogar keinen Code in der Funktion selbst aus. Das Ergebnis ist ein spezielles Objekt namens generator :

>>> generator = generator_function()
>>> generator
<generator object generator_function at 0x10b3f2b90>

Es handelt sich also nicht um eine Generatorfunktion, sondern um einen Generator:

>>> inspect.isgeneratorfunction(generator)
False

>>> import types
>>> isinstance(generator, types.GeneratorType)
True

und Generatorfunktion ist nicht Generator:

>>> isinstance(generator_function, types.GeneratorType)
False

Nur als Referenz wird der tatsächliche Aufruf des Funktionskörpers durch den Verbrauch eines Generators erfolgen, z.

>>> list(generator)
[1, 2]

Siehe auch In Python gibt es eine Möglichkeit zu überprüfen, ob eine Funktion eine "Generatorfunktion" ist, bevor sie aufgerufen wird?

Robert Lujo
quelle
11

Die inspect.isgeneratorFunktion ist in Ordnung, wenn Sie nach reinen Generatoren suchen möchten (dh nach Objekten der Klasse "Generator"). Es wird jedoch zurückgegeben, Falsewenn Sie beispielsweise ein izipiterables Element überprüfen . Eine alternative Möglichkeit zur Suche nach einem verallgemeinerten Generator ist die Verwendung dieser Funktion:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')
Luca Sbardella
quelle
1
Hmm. Dies gibt true für zurück x=iter([1,2]). Mir scheint, es wird wirklich getestet, ob ein Objekt ein Iterator und kein Generator ist. Aber vielleicht ist "Iterator" genau das, was Sie unter "generalisiertem Generator" verstehen.
Josh O'Brien
2
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True
Corey Goldberg
quelle
Dies funktioniert nur, wenn es sich um eine Funktion handelt. Wenn 'foo' ein Generatorobjekt ist, wird 'False' angezeigt. Siehe meine Frage, ich möchte nach Generatorobjekten suchen.
Pushpak Dagade
2

Ich weiß, dass ich überprüfen kann, ob das Objekt eine nächste Methode hat, um ein Generator zu sein, aber ich möchte eine Möglichkeit, mit der ich den Typ eines Objekts bestimmen kann, nicht nur Generatoren.

Tu das nicht. Es ist einfach eine sehr, sehr schlechte Idee.

Tun Sie stattdessen Folgendes:

try:
    # Attempt to see if you have an iterable object.
    for i in some_thing_which_may_be_a_generator:
        # The real work on `i`
except TypeError:
     # some_thing_which_may_be_a_generator isn't actually a generator
     # do something else

Für den unwahrscheinlichen Fall, dass der Hauptteil der for- Schleife auch TypeErrors enthält, gibt es mehrere Möglichkeiten: (1) Definieren Sie eine Funktion, um den Umfang der Fehler zu begrenzen, oder (2) verwenden Sie einen verschachtelten try- Block.

Oder (3) so etwas, um all diese TypeErrors zu unterscheiden, die herumschweben.

try:
    # Attempt to see if you have an iterable object.
    # In the case of a generator or iterator iter simply 
    # returns the value it was passed.
    iterator = iter(some_thing_which_may_be_a_generator)
except TypeError:
     # some_thing_which_may_be_a_generator isn't actually a generator
     # do something else
else:
    for i in iterator:
         # the real work on `i`

Oder (4) die anderen Teile Ihrer Anwendung reparieren, um Generatoren entsprechend bereitzustellen. Das ist oft einfacher als das alles.

S.Lott
quelle
1
Ihre Lösung fängt TypeErrors ab, die vom Hauptteil der for-Schleife ausgelöst werden. Ich habe eine Änderung vorgeschlagen, die dieses unerwünschte Verhalten verhindern würde.
Dünen
Dies ist die pythonischere Art, es zu tun, wenn ich mich nicht irre.
JAB
Wenn Sie jedoch eine Liste von Elementen durchlaufen und mehr von ihnen keine Iteratoren als Iteratoren sind, kann dies sicherlich länger dauern?
Jakob Bowyer
1
@ Jakob Bowyer: Ausnahmen sind schneller als ifAussagen. Und. Diese Art der Mikrooptimierung ist Zeitverschwendung. Korrigieren Sie den Algorithmus, der eine gemischte Tüte von Iteratoren und Nicht-Iteratoren erzeugt, um nur Iteratoren zu erzeugen und sich all diesen Schmerz zu ersparen.
S.Lott
10
Dies würde fälschlicherweise jede Iteration als Generator annehmen.
Balki
2

Sie können den Iterator oder genauer gesagt den Generator aus dem Typisierungsmodul verwenden .

from typing import Generator, Iterator
g = (i for i in range(1_000_000))
print(type(g))
print(isinstance(g, Generator))
print(isinstance(g, Iterator))

Ergebnis:

<class 'generator'>
True
True
user9074332
quelle
1
+1 für eine funktionierende Lösung. Vor diesem Hintergrund typing.TypeVarscheinen die Dokumente für die Klasse von der Verwendung isinstancein Verbindung mit dem typingModul abzuraten : " isinstance(x, T)Wird zur Laufzeit ausgelöst TypeError. Im Allgemeinen isinstance()und issubclass()sollte nicht mit Typen verwendet werden."
Jasha
1

Wenn Sie einen Tornado-Webserver oder einen ähnlichen verwenden, haben Sie möglicherweise festgestellt, dass Servermethoden tatsächlich Generatoren und keine Methoden sind. Dies macht es schwierig, andere Methoden aufzurufen, da Yield innerhalb der Methode nicht funktioniert und Sie daher mit der Verwaltung von Pools verketteter Generatorobjekte beginnen müssen. Eine einfache Methode zum Verwalten von Pools verketteter Generatoren besteht darin, eine Hilfefunktion wie z

def chainPool(*arg):
    for f in arg:
      if(hasattr(f,"__iter__")):
          for e in f:
             yield e
      else:
         yield f

Schreiben Sie jetzt verkettete Generatoren wie

[x for x in chainPool(chainPool(1,2),3,4,chainPool(5,chainPool(6)))]

Erzeugt Ausgabe

[1, 2, 3, 4, 5, 6]

Welches ist wahrscheinlich, was Sie wollen, wenn Sie Generatoren als Thread-Alternative oder ähnliches verwenden möchten.

user6830669
quelle
1

(Ich weiß, dass es ein alter Beitrag ist.) Es ist nicht erforderlich, ein Modul zu importieren. Sie können zu Beginn des Programms ein Objekt zum Vergleich deklarieren:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
kantal
quelle