Python: механизм понимания списка - PullRequest
6 голосов
/ 30 января 2011

При использовании списка или ключевого слова in в контексте цикла for, например:

for o in X:
    do_something_with(o)

или

l=[o for o in X]
  • Как работает механизм in работает?
  • Какие функции \ методы в X он вызывает?
  • Если X может соответствовать более чем одному методу, какой приоритет?
  • Как написать эффективный X, чтобы понимание списка было быстрым?

Ответы [ 6 ]

10 голосов
/ 30 января 2011

Полный, правильный и правильный ответ.

for, как для циклов, так и для списков, вызывает iter() на X.iter() возвратит итерацию, если X имеет либо метод __iter__, либо метод __getitem__.Если он реализует оба, используется __iter__.Если его нет, вы получаете TypeError: 'Nothing' object is not iterable.

. Это реализует __getitem__:

class GetItem(object):
    def __init__(self, data):
        self.data = data

    def __getitem__(self, x):
        return self.data[x]

Использование:

>>> data = range(10)
>>> print [x*x for x in GetItem(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Это пример реализации __iter__:

class TheIterator(object):
    def __init__(self, data):
        self.data = data
        self.index = -1

    # Note: In  Python 3 this is called __next__
    def next(self):
        self.index += 1
        try:
            return self.data[self.index]
        except IndexError:
            raise StopIteration

    def __iter__(self):
        return self

class Iter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return TheIterator(data)

Использование:

>>> data = range(10)
>>> print [x*x for x in Iter(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Как вы видите, вам нужно как реализовать итератор, так и __iter__, который возвращает итератор.

Выможно объединить их:

class CombinedIter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        self.index = -1
        return self

    def next(self):
        self.index += 1
        try:
            return self.data[self.index]
        except IndexError:
            raise StopIteration

Использование:

>>> well, you get it, it's all the same...

Но тогда вы можете использовать только один итератор одновременно.Хорошо, в этом случае вы можете просто сделать это:

class CheatIter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return iter(self.data)

Но это обман, потому что вы просто повторно используете __iter__ метод list.Более простым способом является использование yield и превращение __iter__ в генератор:

class Generator(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        for x in self.data:
            yield x

Это последний способ, который я бы порекомендовал.Легко и эффективно.

5 голосов
/ 30 января 2011

X должно быть повторяемым.Он должен реализовывать __iter__(), который возвращает объект итератора;объект итератора должен реализовывать next(), который возвращает следующий элемент каждый раз, когда он вызывается, или вызывает StopIteration, если следующего элемента нет.

Списки, кортежи и генераторы являются итеративными.* Обратите внимание, что обычный оператор for использует тот же механизм.

4 голосов
/ 30 января 2011

Отвечая на комментарии к вопросу, я могу сказать, что чтение источника не самая лучшая идея в этом случае. Код, отвечающий за выполнение скомпилированного кода ( ceval.c ), не выглядит слишком многословным для человека, который впервые видит источники Python. Вот фрагмент, представляющий итерацию для циклов for:

   TARGET(FOR_ITER)
        /* before: [iter]; after: [iter, iter()] *or* [] */
        v = TOP();

        /*
          Here tp_iternext corresponds to next() in Python
        */
        x = (*v->ob_type->tp_iternext)(v); 
        if (x != NULL) {
            PUSH(x);
            PREDICT(STORE_FAST);
            PREDICT(UNPACK_SEQUENCE);
            DISPATCH();
        }
        if (PyErr_Occurred()) {
            if (!PyErr_ExceptionMatches(
                            PyExc_StopIteration))
                break;
            PyErr_Clear();
        }
        /* iterator ended normally */
        x = v = POP();
        Py_DECREF(v);
        JUMPBY(oparg);
        DISPATCH();

Чтобы узнать, что на самом деле здесь происходит, вам нужно погрузиться в кучу других файлов, многословность которых не намного лучше. Таким образом, я думаю, что в таких случаях первыми должны идти документация и такие сайты, как SO, а источник должен проверяться только на предмет раскрытых деталей реализации.

3 голосов
/ 30 января 2011

X должен быть повторяемым объектом, что означает, что он должен иметь метод __iter__().

Итак, чтобы начать цикл for..in или понимание списка, сначала нужно X.__iter__() метод вызывается для получения объекта итератора;затем метод next() этого объекта вызывается для каждой итерации до тех пор, пока не будет увеличено значение StopIteration, после чего итерация останавливается.

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

2 голосов
/ 30 января 2011

Может быть, это поможет (учебник http://docs.python.org/tutorial/classes.html Раздел 9.9):

За кулисами оператор for вызывает iter () для объекта контейнера.Функция возвращает объект-итератор, который определяет метод next (), который обращается к элементам контейнера по одному.Когда больше нет элементов, next () вызывает исключение StopIteration, которое сообщает циклу for завершиться.

0 голосов
/ 30 января 2011

Чтобы ответить на ваши вопросы:

Как работает механизм, лежащий в основе?

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

Какие функции \ методы в X он вызывает?

Как отмечено в комментарии ниже, он вызывает iter(X), чтобы получить итератор.Если X имеет определенную методическую функцию __iter__(), она будет вызвана для возврата итератора;в противном случае, если X определяет __getitem__(), он будет вызываться повторно для итерации по X.См. Документацию Python для iter() здесь: http://docs.python.org/library/functions.html#iter

Если X может соответствовать более чем одному методу, какой приоритет?

Я не уверен, что ваш вопрос здесь,точно, но у Python есть стандартные правила, как он разрешает имена методов, и они соблюдаются здесь.Вот обсуждение этого:

Порядок разрешения методов (MRO) в классах Python нового стиля

Как написать эффективный X, чтобы понимание списка было быстрым?

Я предлагаю вам прочитать больше об итераторах и генераторах в Python.Один простой способ сделать любую итерацию поддержки класса - создать функцию генератора для iter ().Вот обсуждение генераторов:

http://linuxgazette.net/100/pramode.html

...