Làm thế nào để kiểm tra nếu một đối tượng là một đối tượng tạo trong python?


157

Trong python, làm cách nào để kiểm tra xem một đối tượng có phải là đối tượng tạo không?

Đang thử cái này -

>>> type(myobject, generator)

đưa ra lỗi -

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

(Tôi biết tôi có thể kiểm tra xem đối tượng có nextphương thức để nó là trình tạo không, nhưng tôi muốn một số cách sử dụng để tôi có thể xác định loại của bất kỳ đối tượng nào, không chỉ máy phát điện.)


4
Vấn đề thực tế nào bạn đang cố gắng giải quyết? Đăng nhiều ngữ cảnh, có thể có một cách thông minh hơn. Tại sao bạn cần biết nếu nó là một máy phát điện?
Daenyth

7
from types import GeneratorType;type(myobject, GeneratorType)sẽ cung cấp cho bạn kết quả thích hợp cho các đối tượng của lớp 'trình tạo'. Nhưng như Daenyth ngụ ý, đó không hẳn là con đường đúng đắn.
JAB

7
Nếu bạn đang kiểm tra __next__, bạn thực sự chấp nhận bất kỳ trình lặp nào, không chỉ các trình tạo - rất có thể là những gì bạn muốn.

2
Như thường lệ, điểm thực sự của việc biết liệu thứ gì đó có phải là máy phát điện hay không là có thể tránh được chúng, vì lý do muốn lặp đi lặp lại trên cùng một bộ sưu tập nhiều lần.
Ian

2
Đối với những người tự hỏi về các trường hợp sử dụng, điều này có thể hữu ích khi bạn cần phải biết nếu lặp sẽ được tiêu thụ (ví dụ: nếu chức năng của bạn chấp nhận bất kỳ iterator nhưng cần phải lặp nhiều hơn một lần, bạn sẽ muốn trở thành hiện thực nó trước khi lặp lại)
wbadart

Câu trả lời:


227

Bạn có thể sử dụng GeneratorType từ các loại:

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

5
Thật không may, điều này không hoạt động đối với các lớp trình tạo (ví dụ: các đối tượng ánh xạ hoặc bộ lọc).
Ricardo Cruz

Có lẽ isinstance(gen, (types.GeneratorType, map, filter))là hữu ích để cũng phát hiện mapfilter. Điều này vẫn sẽ không bao gồm các iterables và iterators khác.
jlh

38

Ý bạn là chức năng tạo? sử dụng inspect.isgeneratorfunction.

BIÊN TẬP :

nếu bạn muốn một đối tượng trình tạo, bạn có thể sử dụng notify.isgenerator như được chỉ ra bởi JAB trong bình luận của mình.


1
chức năng máy phát không phải là đối tượng máy phát; xem câu trả lời của @ utdemir
Piotr Findeisen

5
@Piotr: Trong trường hợp bạn sử dụng inspect.isgenerator.
JAB

@JAB, @Piotr: Được phản ánh để giải quyết tất cả các khả năng của OP có nghĩa là gì, cảm ơn JAB :)
mouad

1
Lưu ý: nếu bạn chỉ cần thử nghiệm này, bạn có thể tránh một chi phí nhỏ bằng cách sử dụng giải pháp @utdemirinspect.isgeneratorđây chỉ là cách viết tắt của : isinstance(object, types.GeneratorType).
bufh

Xem câu trả lời @RobertLujo để phân biệt giữa đối tượng trình tạo và chức năng trình tạo. stackoverflow.com/a/32380774/3595112
ngành công

24

Tôi nghĩ điều quan trọng là phải phân biệt giữa các hàm tạo và các trình tạo (kết quả của hàm tạo):

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

gọi bộ tạo_feft sẽ không mang lại kết quả bình thường, thậm chí nó sẽ không thực thi bất kỳ mã nào trong hàm, kết quả sẽ là đối tượng đặc biệt gọi là trình tạo :

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

Vì vậy, nó không phải là chức năng của trình tạo, mà là trình tạo:

>>> inspect.isgeneratorfunction(generator)
False

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

và chức năng của trình tạo không phải là trình tạo:

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

chỉ để tham khảo, cuộc gọi thực tế của thân hàm sẽ xảy ra bằng cách tiêu thụ trình tạo, ví dụ:

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

Xem thêm Trong python có cách nào để kiểm tra xem một hàm có phải là "hàm tạo" không trước khi gọi nó?


11

Các inspect.isgeneratorchức năng là tốt nếu bạn muốn kiểm tra máy phát điện thuần túy (tức là các đối tượng của lớp "máy phát điện"). Tuy nhiên, nó sẽ trở lại Falsenếu bạn kiểm tra, ví dụ, một lần iziplặp. Một cách khác để kiểm tra trình tạo tổng quát là sử dụng chức năng này:

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

1
Hừm. Điều này trả về đúng cho x=iter([1,2]). Dường như với tôi nó thực sự đang kiểm tra xem một đối tượng có phải là một trình vòng lặp hay không, không phải là một trình tạo. Nhưng có lẽ "iterator" chính xác là những gì bạn muốn nói về "trình tạo tổng quát".
Josh O'Brien

3

Bạn có thể sử dụng Iterator hoặc cụ thể hơn là Trình tạo từ mô-đun .

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

kết quả:

<class 'generator'>
True
True

1
+1 cho một giải pháp làm việc. Điều này đang được nói, các tài liệu cho typing.TypeVarlớp dường như không khuyến khích việc sử dụng isinstancekết hợp với typingmô-đun: "Trong thời gian chạy, isinstance(x, T)sẽ nâng lên TypeError. Nói chung, isinstance()issubclass()không nên được sử dụng với các loại."
Jasha

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

Điều này chỉ hoạt động nếu nó là một chức năng. Nếu 'foo' là đối tượng trình tạo, nó hiển thị 'Sai'. Xem câu hỏi của tôi, tôi muốn thực hiện kiểm tra cho các đối tượng máy phát điện.
Pushpak Dagade

2

Tôi biết tôi có thể kiểm tra xem đối tượng có phương thức tiếp theo để nó là trình tạo không, nhưng tôi muốn một số cách sử dụng để tôi có thể xác định loại của bất kỳ đối tượng nào, không chỉ máy phát điện.

Đừng làm điều này. Nó chỉ đơn giản là một ý tưởng rất, rất xấu.

Thay vào đó, hãy làm điều này:

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

Trong trường hợp không thể xảy ra, phần thân của vòng lặp for cũng có TypeErrors, có một số lựa chọn: (1) xác định hàm để giới hạn phạm vi của các lỗi hoặc (2) sử dụng khối thử lồng nhau .

Hoặc (3) một cái gì đó như thế này để phân biệt tất cả những TypeErrorthứ đang trôi nổi xung quanh.

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`

Hoặc (4) sửa các phần khác trong ứng dụng của bạn để cung cấp máy phát điện phù hợp. Điều đó thường đơn giản hơn tất cả những điều này.


1
Giải pháp của bạn sẽ bắt TypeErrors được ném bởi phần thân của vòng lặp for. Tôi đã đề xuất một chỉnh sửa sẽ ngăn chặn hành vi không mong muốn này.
Cồn cát

Đây là cách làm Pythonic nhiều hơn, nếu tôi không nhầm.
JAB

Mặc dù, nếu bạn đang lặp qua một danh sách các mục và nhiều trong số chúng không phải là trình vòng lặp hơn là các trình vòng lặp thì điều này có thể mất nhiều thời gian hơn không?
Jakob Bowyer

1
@Jakob Bowyer: Ngoại lệ nhanh hơn ifbáo cáo. Và. Đó là loại tối ưu hóa vi mô là một sự lãng phí thời gian. Khắc phục thuật toán tạo ra một túi hỗn hợp gồm các trình lặp và không lặp để chỉ tạo ra các trình vòng lặp và tự cứu mình khỏi tất cả những nỗi đau này.
S.Lott

10
Điều này sẽ nhầm tưởng bất kỳ lặp đi lặp lại như là một máy phát điện.
balki

1

Nếu bạn đang sử dụng máy chủ web lốc xoáy hoặc tương tự, bạn có thể thấy rằng các phương thức máy chủ thực sự là các trình tạo và không phải là các phương thức. Điều này gây khó khăn cho việc gọi các phương thức khác vì năng suất không hoạt động bên trong phương thức và do đó bạn cần bắt đầu quản lý nhóm các đối tượng trình tạo chuỗi. Một phương pháp đơn giản để quản lý nhóm các trình tạo chuỗi là tạo ra một chức năng trợ giúp như

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

Bây giờ viết các trình tạo chuỗi như

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

Sản xuất đầu ra

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

Đó có lẽ là những gì bạn muốn nếu bạn muốn sử dụng máy phát điện như là một chủ đề thay thế hoặc tương tự.


1

(Tôi biết đó là một bài viết cũ.) Không cần nhập mô-đun, bạn có thể khai báo một đối tượng để so sánh khi bắt đầu chương trình:

gentyp= type(1 for i in "")                                                                                          
       ...
type(myobject) == gentyp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.