Динамически добавлять метод len?

#python #reflection

#python #отражение

Вопрос:

Как мне добавить __len__(...) к существующему объекту len(...) понравившимся способом?

Вариант использования заключается в запоминании длины генератора после его первой итерации таким образом, чтобы не требовалось писать / изменять код между сайтами итераций.

Вот что у меня есть до сих пор:

 def generator2iterator(generator):
    class Iterator(object):
        def __iter__(self):
            i = 0
            for e in generator():
                i  = 1
                yield e
            #self.__len__ = types.MethodType(lambda self: i, self)
            self.__len__ = (lambda self: i).__get__(self)
    return Iterator()

def test_generator():
    yield 3

iterator = generator2iterator(test_generator)

print(list(iterator))
# [3]

print(iterator.__len__())
# 1

print(len(iterator))
# TypeError: object of type 'Foo' has no len()
 

Ответ №1:

Определите атрибут, который содержит длину, и верните его для __len__ .

 def generator2iterator(generator, length=None):
    class Iterator(object):
        def __iter__(self):
            for i, e in enumerate(generator(), 1):
                self._len = i
                yield e
            #self.__len__ = types.MethodType(lambda self: i, self)

        def __len__(self):
            if not hasattr(self, '_len'):
                raise TypeError("object of type 'Iterator' has no defined len() yet.")
            return self._len
    return Iterator()

# examples:
## define a generator with 10 elements
def g():
    for i in range(10):
        yield i

# pass it to the iterator
it = generator2iterator(g)

# check len
len(it)
TypeError: object of type 'Iterator' has no defined len() yet.

# run the iterator
for _ in it:
    pass

# check the length
len(it)
# returns:
10
 

Комментарии:

1. Тогда было бы время, для которого len(it) дается неправильный ответ.

2. Вы не всегда можете заранее определить длину генератора. Можете ли вы привести пример ожидаемого результата?

3. Я бы хотел __len__ , чтобы он не был определен (вызовы len завершаются ошибкой, как и должны быть) до первой итерации, после чего мы знаем длину генератора — поэтому (возможно) динамически добавляем метод.

4. о! в этом случае удалите определение __init__ и вызовите ошибку. смотрите мое обновление