Bagaimana cara memeriksa apakah suatu objek adalah objek generator di python?

157

Dengan python, bagaimana cara memeriksa apakah suatu objek adalah objek generator?

Mencoba ini -

>>> type(myobject, generator)

memberikan kesalahan -

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

(Saya tahu saya dapat memeriksa apakah objek memiliki nextmetode untuk menjadi generator, tetapi saya ingin beberapa cara menggunakan yang saya dapat menentukan jenis objek apa pun, bukan hanya generator.)

Pushpak Dagade
sumber
4
Apa masalah sebenarnya yang Anda coba selesaikan? Posting lebih banyak konteks, mungkin ada cara yang lebih cerdas. Mengapa Anda perlu tahu apakah itu generator?
Daenyth
7
from types import GeneratorType;type(myobject, GeneratorType)akan memberi Anda hasil yang tepat untuk objek 'generator' kelas. Tetapi seperti yang ditunjukkan oleh Daenyth, itu belum tentu cara yang tepat untuk pergi.
JAB
7
Jika Anda memeriksa __next__, Anda sebenarnya menerima iterator apa pun, bukan hanya generator - yang sangat mungkin apa yang Anda inginkan.
2
Seringkali tidak, poin sebenarnya untuk mengetahui apakah sesuatu adalah generator adalah untuk dapat menghindarinya, karena keinginan untuk beralih pada koleksi yang sama beberapa kali.
Ian
2
Bagi orang-orang yang bertanya-tanya tentang use case, ini bisa berguna ketika Anda perlu tahu apakah iterator akan dikonsumsi (mis. Jika fungsi Anda menerima iterator tetapi perlu mengulangi lebih dari sekali, Anda ingin mematerialisasikannya sebelum iterasi)
wbadart

Jawaban:

227

Anda dapat menggunakan GeneratorType dari jenis:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True
utdemir
sumber
5
Sayangnya ini tidak berfungsi untuk kelas generator (misalnya, memetakan atau memfilter objek).
Ricardo Cruz
Mungkin isinstance(gen, (types.GeneratorType, map, filter))bermanfaat juga mendeteksi mapdan filter. Ini masih belum termasuk iterables dan iterators lainnya.
jlh
38

Maksud Anda fungsi generator? gunakan inspect.isgeneratorfunction.

EDIT:

jika Anda menginginkan objek generator, Anda dapat menggunakan inspect.isgenerator seperti yang ditunjukkan oleh JAB dalam komentarnya.

mouad
sumber
1
fungsi generator bukan objek generator; lihat jawaban @ utdemir
Piotr Findeisen
5
@Piotr: Dalam hal ini Anda menggunakan inspect.isgenerator.
JAB
@JAB, @Piotr: Tercermin untuk membahas semua kemungkinan dari apa yang bisa OP maksudkan, terima kasih JAB :)
mouad
1
Catatan: jika Anda hanya perlu tes ini, Anda dapat menghindari overhead kecil dengan menggunakan @utdemir solusi karena inspect.isgeneratorhanya singkatan untuk: isinstance(object, types.GeneratorType).
bufh
Lihat jawaban @RobertLujo untuk perbedaan antara objek generator dan fungsi generator. stackoverflow.com/a/32380774/3595112
industryworker3595112
24

Saya pikir penting untuk membuat perbedaan antara fungsi generator dan generator (hasil fungsi generator):

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

memanggil generator_function tidak akan menghasilkan hasil normal, bahkan tidak akan mengeksekusi kode apa pun dalam fungsi itu sendiri, hasilnya akan menjadi objek khusus yang disebut generator :

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

jadi ini bukan fungsi generator, tapi generator:

>>> inspect.isgeneratorfunction(generator)
False

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

dan fungsi generator bukan generator:

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

hanya untuk referensi, panggilan fungsi tubuh yang sebenarnya akan terjadi dengan mengkonsumsi generator, misalnya:

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

Lihat juga Dalam python apakah ada cara untuk memeriksa apakah suatu fungsi merupakan "fungsi generator" sebelum memanggilnya?

Robert Lujo
sumber
11

The inspect.isgeneratorFungsi baik-baik saja jika Anda ingin memeriksa generator murni (yaitu objek dari "generator" kelas). Namun itu akan kembali Falsejika Anda memeriksa, misalnya, izipiterable. Cara alternatif untuk memeriksa generator yang umum adalah dengan menggunakan fungsi ini:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')
Luca Sbardella
sumber
1
Hmm. Ini mengembalikan true untuk x=iter([1,2]). Menurut saya itu benar-benar menguji apakah suatu objek adalah iterator , bukan generator. Tapi mungkin "iterator" persis seperti yang Anda maksud dengan "generator yang digeneralisasi".
Josh O'Brien
3

Anda bisa menggunakan Iterator atau lebih spesifik, Generator dari modul pengetikan .

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

hasil:

<class 'generator'>
True
True
pengguna9074332
sumber
1
+1 untuk solusi yang berfungsi. Makhluk ini mengatakan, dokumen untuk typing.TypeVarkelas tampaknya mencegah penggunaan isinstancebersama dengan typingmodul: "Pada saat runtime, isinstance(x, T)akan meningkat TypeError. Secara umum, isinstance()dan issubclass()tidak boleh digunakan dengan tipe."
Jasha
2
>>> import inspect
>>> 
>>> def foo():
...   yield 'foo'
... 
>>> print inspect.isgeneratorfunction(foo)
True
Corey Goldberg
sumber
Ini hanya berfungsi jika itu adalah fungsi. Jika 'foo' adalah objek generator, itu menunjukkan 'Salah'. Lihat pertanyaan saya, saya ingin memeriksa objek generator.
Pushpak Dagade
2

Saya tahu saya dapat memeriksa apakah objek memiliki metode berikutnya untuk menjadi generator, tetapi saya ingin beberapa cara menggunakan yang saya dapat menentukan jenis objek apa pun, bukan hanya generator.

Jangan lakukan ini. Itu hanya ide yang sangat, sangat buruk.

Sebaliknya, lakukan ini:

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

Dalam hal tidak mungkin bahwa tubuh for for loop juga memiliki TypeErrors, ada beberapa pilihan: (1) menentukan fungsi untuk membatasi ruang lingkup kesalahan, atau (2) menggunakan blok percobaan bersarang .

Atau (3) sesuatu seperti ini untuk membedakan semua ini TypeErroryang beredar.

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`

Atau (4) perbaiki bagian lain dari aplikasi Anda untuk menyediakan generator dengan tepat. Itu seringkali lebih sederhana dari semua ini.

S.Lott
sumber
1
Solusi Anda akan menangkap TypeErrors yang dilemparkan oleh tubuh for for. Saya telah mengusulkan suntingan yang akan mencegah perilaku yang tidak diinginkan ini.
Bukit
Ini adalah cara yang lebih Pythonic untuk melakukannya, jika saya tidak salah.
JAB
Meskipun, jika Anda mengulangi daftar item dan lebih banyak dari mereka bukan iterator daripada iterator maka ini bisa memakan waktu lebih lama tentunya?
Jakob Bowyer
1
@Jakob Bowyer: Pengecualian lebih cepat dari pada ifpernyataan. Dan. Optimalisasi mikro semacam itu hanya buang-buang waktu saja. Perbaiki algoritma yang menghasilkan kantung iterator dan non-iterator campuran untuk menghasilkan iterator saja dan selamatkan diri Anda dari semua rasa sakit ini.
S.Lott
10
Ini keliru akan menganggap setiap iterable sebagai generator.
balki
1

Jika Anda menggunakan server web tornado atau sejenisnya, Anda mungkin menemukan bahwa metode server sebenarnya adalah generator dan bukan metode. Ini membuatnya sulit untuk memanggil metode lain karena hasil tidak bekerja di dalam metode dan karena itu Anda perlu mulai mengelola kumpulan objek generator yang dirantai. Metode sederhana untuk mengelola kumpulan generator berantai adalah dengan membuat fungsi bantuan seperti

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

Sekarang menulis generator berantai seperti

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

Menghasilkan output

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

Yang mungkin adalah apa yang Anda inginkan jika Anda ingin menggunakan generator sebagai alternatif atau sejenisnya.

pengguna6830669
sumber
1

(Saya tahu ini adalah posting lama.) Tidak perlu mengimpor modul, Anda dapat mendeklarasikan objek untuk perbandingan di awal program:

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