Как проверить, действительно ли итератор является контейнером итератора?

у меня есть фиктивный пример контейнера итератора ниже (реальный читает файл, слишком большой, чтобы поместиться в памяти):

class DummyIterator:
    def __init__(self, max_value):
        self.max_value = max_value

    def __iter__(self):
        for i in range(self.max_value):
            yield i

def regular_dummy_iterator(max_value):
    for i in range(max_value):
        yield i

Это позволяет мне перебирать значение еще раз чтобы я мог реализовать что-то вроде этого:

def normalise(data):
    total = sum(i for i in data)
    for val in data:
        yield val / total

# this works when I call next()
normalise(DummyIterator(100))

# this doesn't work when I call next()
normalise(regular_dummy_iterator(100))

Как проверить функцию normalise, что мне передается контейнер итератора, а не обычный генератор?

2 ответов


прежде всего: нет такой вещи, как итератор контейнера. У вас есть типа Iterable.

iterable создает итератор. Любой итератор-это тоже итерируемые, но производит как итератор:

>>> list_iter = iter([])
>>> iter(list_iter) is list_iter
True

у вас нет итератора, если iter(ob) is ob тест false.


вы можете проверить есть ли у вас итератор (отсчитывается с момента next поднимает StopIteration исключение) vs просто iterable (возможно, может повторяться несколько раз) с помощью collections.abcмодуль. Вот пример:

from collections.abc import Iterable, Iterator

def my_iterator(): 
    yield 1

i = my_iterator()
a = []

isinstance(i, Iterator) # True
isinstance(a, Iterator) # False

что составляет my_iterator() an Iterator является наличие как __next__ и __iter__ магические методы (и кстати, в основном то, что происходит за кулисами, когда вы называете isinstance на collections.abc аннотация базовый класс-это тест на наличие определенных магических методов).

обратите внимание, что итератор тоже Iterable, как и пустой список (т. е. оба имеют __iter__ магический метод):

isinstance(i, Iterable) # True
isinstance(a, Iterable) # True

также обратите внимание,как было указано в ответ Мартин Питерс', что при применении generic iter() функция для обоих, вы получаете итератор:

isinstance(iter(my_iterator()), Iterator) # True
isinstance(iter([])), Iterator) # True

разница здесь между [] и my_iterator() это iter(my_iterator()) возвращает как итератор, тогда как iter([]) производит новый итератор каждый раз.

как уже упоминалось в том же ответе MP, ваш объект выше не является "контейнером итератора"."Это итерируемый объект, т. е. "повторяемое". Независимо от того," содержит " ли он что-то, на самом деле не связано; понятие содержания представлено абстрактным базовым классом Container. А Container может быть iterable, но это не обязательно должно быть.