Jak sprawdzić, czy obiekt jest obiektem generatora w Pythonie?


157

Jak w Pythonie sprawdzić, czy obiekt jest obiektem generatora?

Próbuję tego -

>>> type(myobject, generator)

podaje błąd -

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

(Wiem, że mogę sprawdzić, czy obiekt ma nextmetodę, aby był generatorem, ale chcę mieć jakiś sposób, za pomocą którego mogę określić typ dowolnego obiektu, nie tylko generatorów.)


4
Jaki problem próbujesz rozwiązać? Opublikuj więcej kontekstu, może być mądrzejszy sposób. Dlaczego musisz wiedzieć, czy to generator?
Daenyth

7
from types import GeneratorType;type(myobject, GeneratorType)da ci właściwy wynik dla obiektów klasy „generator”. Ale jak sugeruje Daenyth, niekoniecznie jest to właściwa droga.
JAB

7
Jeśli sprawdzasz __next__, w rzeczywistości akceptujesz dowolny iterator, a nie tylko generatory - co jest bardzo prawdopodobne, że chcesz.

2
Tak często, jak nie, prawdziwym celem wiedzy, czy coś jest generatorem, jest możliwość ich uniknięcia ze względu na chęć wielokrotnego iterowania tego samego zbioru.
Ian

2
Dla osób zastanawiających się nad przypadkiem użycia może to być przydatne, gdy chcesz wiedzieć, czy iterator zostanie zużyty (np. Jeśli twoja funkcja akceptuje dowolny iterator, ale musi wykonać iterację więcej niż raz, będziesz chciał zmaterializować go przed iteracją)
wbadart

Odpowiedzi:


227

Możesz użyć GeneratorType z typów:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

5
To niestety nie działa w przypadku klas generatorów (na przykład obiektów map lub filtrów).
Ricardo Cruz

Być może isinstance(gen, (types.GeneratorType, map, filter))warto również wykryć mapi filter. Jednak nadal nie będzie to obejmować innych iterowalnych i iteratorów.
jlh

38

Masz na myśli funkcje generatora? używać inspect.isgeneratorfunction.

EDYTOWAĆ :

jeśli potrzebujesz obiektu generatora, możesz użyć inspect.isgenerator, jak wskazał JAB w swoim komentarzu.


1
funkcja generatora nie jest obiektem generatora; zobacz odpowiedź @ utdemir
Piotr Findeisen

5
@Piotr: W takim przypadku używasz inspect.isgenerator.
JAB

@JAB, @Piotr: Refleksja na temat wszystkich możliwości tego, co może oznaczać PO, dzięki JAB :)
mouad

1
Uwaga: jeśli potrzebna jest tylko ten test, można uniknąć niewielki narzut za pomocą @utdemir rozwiązanie , ponieważ inspect.isgeneratorjest tylko skrótem do: isinstance(object, types.GeneratorType).
bufh

Zobacz odpowiedź @RobertLujo, aby zobaczyć rozróżnienie między obiektem generatora a funkcją generatora. stackoverflow.com/a/32380774/3595112
industryworker3595112

24

Myślę, że ważne jest, aby dokonać rozróżnienia między funkcjami generatorów a generatorami (wynik funkcji generatora):

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

wywołanie funkcji generator_function nie przyniesie normalnego wyniku, nawet nie wykona żadnego kodu w samej funkcji, wynikiem będzie specjalny obiekt o nazwie generator :

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

więc nie jest to funkcja generatora, ale generator:

>>> inspect.isgeneratorfunction(generator)
False

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

a funkcja generatora nie jest generatorem:

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

tylko w celach informacyjnych, faktyczne wywołanie treści funkcji nastąpi poprzez zużycie generatora, np .:

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

Zobacz także Czy w pythonie istnieje sposób sprawdzenia, czy funkcja jest „funkcją generatora” przed jej wywołaniem?


11

inspect.isgeneratorFunkcja jest w porządku, jeśli chcesz sprawdzić czystych generatorów (czyli obiektów „generator” klasa). Jednak zwróci, Falsejeśli izipzaznaczysz na przykład iterowalny plik . Alternatywnym sposobem sprawdzenia generatora uogólnionego jest użycie tej funkcji:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')

1
Hmm. To zwraca prawdę dla x=iter([1,2]). Wydaje mi się, że to naprawdę sprawdza, czy obiekt jest iteratorem , a nie generatorem. Ale może „iterator” jest dokładnie tym, co masz na myśli, mówiąc o „generatorze uogólnionym”.
Josh O'Brien

3

Możesz użyć Iteratora lub dokładniej Generatora z modułu pisania .

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))

wynik:

<class 'generator'>
True
True

1
+1 za działające rozwiązanie. Mając to na uwadze, dokumentacja dla typing.TypeVarklasy wydaje się zniechęcać do używania isinstancew połączeniu z typingmodułem: „W czasie wykonywania isinstance(x, T)spowoduje podniesienie TypeError. Ogólnie isinstance()i issubclass()nie powinno się ich używać z typami”.
Jasha

2
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True

Działa to tylko wtedy, gdy jest to funkcja. Jeśli „foo” jest obiektem generatora, pokazuje „False”. Zobacz moje pytanie, chcę sprawdzić obiekty generatora.
Pushpak Dagade

2

Wiem, że mogę sprawdzić, czy obiekt ma następną metodę, aby był generatorem, ale chcę mieć jakiś sposób, za pomocą którego mogę określić typ dowolnego obiektu, nie tylko generatorów.

Nie rób tego. To po prostu bardzo, bardzo zły pomysł.

Zamiast tego zrób to:

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

W mało prawdopodobnym przypadku, gdy treść pętli for również ma TypeErrors, istnieje kilka możliwości: (1) zdefiniowanie funkcji ograniczającej zakres błędów lub (2) użycie zagnieżdżonego bloku try .

Lub (3) coś takiego, aby rozróżnić wszystkie te TypeErrorpływające wokół.

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`

Lub (4) napraw inne części aplikacji, aby odpowiednio zapewnić generatory. To często prostsze niż to wszystko.


1
Twoje rozwiązanie przechwyci TypeErrors wyrzucone przez treść pętli for. Zaproponowałem edycję, która zapobiegłaby temu niepożądanemu zachowaniu.
Wydmy

To jest bardziej Pythonowy sposób na zrobienie tego, jeśli się nie mylę.
JAB

Chociaż, jeśli iterujesz po liście elementów i więcej z nich nie jest iteratorami niż iteratorami, to z pewnością może to zająć więcej czasu?
Jakob Bowyer

1
@Jakob Bowyer: Wyjątki są szybsze niż ifinstrukcje. I. Taka mikro-optymalizacja to strata czasu. Napraw algorytm, który tworzy mieszany zestaw iteratorów i nie-iteratorów, aby wytwarzać tylko iteratory i oszczędzić sobie całego tego bólu.
S.Lott

10
To błędnie zakładałoby, że jakikolwiek iterowalny jest generator.
balki

1

Jeśli używasz serwera WWW tornado lub podobnego, być może zauważyłeś, że metody serwera są w rzeczywistości generatorami, a nie metodami. Utrudnia to wywoływanie innych metod, ponieważ yield nie działa wewnątrz metody i dlatego musisz zacząć zarządzać pulami połączonych obiektów generatora. Prostą metodą zarządzania pulami połączonych generatorów jest utworzenie funkcji pomocy, takiej jak

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

Teraz piszemy generatory łańcuchowe, takie jak

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

Produkuje wynik

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

Co prawdopodobnie jest tym, czego chcesz, jeśli chcesz używać generatorów jako alternatywy dla wątku lub podobnej.


1

(Wiem, że to stary post.) Nie ma potrzeby importowania modułu, możesz zadeklarować obiekt do porównania na początku programu:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.