list (y) поведение "неправильно" при первом вызове - PullRequest
1 голос
/ 16 сентября 2010

У меня есть итератор с определенным методом __len__. Вопросы:

Если вы вызываете list (y) и у y определен метод __len__, то вызывается __len__.

1) Почему?

В моем выводе вы увидите, что len (list (y)) равен 0 с первой попытки. Если вы посмотрите на вывод списка, вы увидите, что при первом вызове я получаю пустой список, а при втором вызове получаю «правильный» список.

2) Почему он вообще возвращает список нулевой длины?

3) Почему длина списка корректируется при всех последующих вызовах?

Также обратите внимание, что вызов enumerate не является проблемой. Класс C делает то же самое, но использует цикл while и вызывает next ().

Код:

showcalls = False

class A(object):
    _length = None
    def __iter__(self):
        if showcalls:
            print "iter"
        self.i = 0
        return self        
    def next(self):
        if showcalls:
            print "next"
        i = self.i + 1
        self.i = i
        if i > 2:
            raise StopIteration
        else:
            return i

class B(A):
    def __len__(self):
        if showcalls:
            print "len"
        if self._length is None:
            for i,x in enumerate(self):
                pass
            self._length = i
            return i
        else:
            return self._length

class C(A):
    def __len__(self):
        if showcalls:
            print "len"
        if self._length is None:
            i = 0
            while True:
                try:
                    self.next()
                except StopIteration:
                    self._length = i
                    return i
                else:
                    i += 1
        else:
            return self._length

if __name__ == '__main__':
    a = A()
    print len(list(a)), len(list(a)), len(list(a))
    print
    b = B()
    print len(list(b)), len(list(b)), len(list(b))
    print
    c = C()
    print len(list(c)), len(list(c)), len(list(c))

Выход:

2 2 2

0 2 2

0 2 2

1 Ответ

6 голосов
/ 16 сентября 2010

Если вы звоните в список (у) и у вас есть len метод определен, затем вызывается len . почему?

Поскольку быстрее построить итоговый список с конечной длиной, если он известен с самого начала, чем начинать с пустого списка и добавлять по одному элементу за раз. И __len__ является и должен быть на 100% гарантированно надежным.

IOW, не реализует специальные методы, такие как __len__, если и когда вы не можете вернуть надежное значение.

Что касается второго вопроса, ваши реализации __len__ сломаны, потому что они потребляют итератор (и не возвращают его в его первоначальное состояние) - поэтому они не оставляют никаких элементов для следования .next вызывает, поэтому конструктор list получает StopIteration и решает, что ваш __len__ был просто ненадежным (к сожалению, он слабее, чем бедный list может догадаться ...! -).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...