Введение и анатомия петли For
Итак, мы начнем с некоторых основ, чтобы все были на одной странице. Основная анатомия петли for немного похожа на:
for x in y: do_stuff(x,y)
do_stuff(x,y)
– это вызов функции. Если вы еще не знаете, как работают функции, то стоит посмотреть на это, прежде чем продолжить здесьy
является итерируемым . Мы немного разберемся в деталях, что это значит. На данный момент итерабель-это все, что вы можете повторить (не паникуйте)x
является фиктивной переменной и может принимать несколько различных форм
Вот несколько примеров циклов for, которые должны закрепить основные понятия:
Диапазон
range
довольно полезен, если мы хотим сделать что-то некоторое целое число раз
for i in range(4): print i
Это выводит:
0 1 2 3
В этом примере используется range
в его простейшей форме. Для получения дополнительной информации о том, как работает range
, введите следующее в командной строке python:
help(range)
Строки
Простые старые строки также итеративны.
for s in "hello": print s
Это выводит:
h e l l o
Списки
Это, вероятно, самые очевидные из итераций.
for x in [None,3,4.5,"foo",lambda : "moo",object,object()]: print "{0} ({1})".format(x,type(x))
Это выводит:
None () 3 ( ) 4.5 ( ) foo ( ) at 0x7feec7fa7578> ( ) ( )
Это может показаться сложным, но главная идея заключается в том, что список может содержать все, что угодно, и имеет подразумеваемый порядок.
Кортежи
Кортежи отличаются от списков некоторыми фундаментальными способами, которые выходят за рамки данного руководства. Вы заметите, что итерация в следующем примере использует круглые скобки вместо квадратных и что выходные данные такие же, как и в приведенном выше примере списка.
for x in (None,3,4.5,"foo",lambda : "moo",object,object()): print "{0} ({1})".format(x,type(x))
Это выводит:
None () 3 ( ) 4.5 ( ) foo ( ) at 0x7feec7fa7578> ( ) ( )
Словари
Словарь-это неупорядоченный список пар ключ-значение. Когда вы перебираете словарь с помощью цикла for, ваша фиктивная переменная будет заполнена различными ключами.
d = { 'apples' : 'tasty', 'bananas' : 'the best', 'brussel sprouts' : 'evil', 'cauliflower' : 'pretty good' } for sKey in d: print "{0} are {1}".format(sKey,d[sKey])
Это выводит:
brussel sprouts are evil apples are tasty cauliflower are pretty good bananas are the best
Но, может быть, не совсем в таком порядке. Повторяю: словари неупорядочены!
Для расширенных циклов: Использование break и continue
Иногда приходится останавливать петлю. Это достигается с помощью оператора break
. Вот простой пример:
for i in range(4000000000): print i if i>=2: break
Это приведет к выходу:
0 1 2
Оператор break смотрит на ближайший содержащий конец цикла и вызывает его выход. Он также работает для циклов while. Вот еще один пример, показывающий, как это будет работать с вложенными циклами
for ii in range(2): print "outer loop iteration" for i in range(4000000000): # inner loop print i if i>=2: break
Это выводит:
outer loop iteration 0 1 2 outer loop iteration 0 1 2
Другой распространенный сценарий-необходимость пропустить остальную часть текущей итерации цикла, но остаться в цикле. Это достигается с помощью оператора continue
. Как и оператор break
, он также смотрит только на ближайший содержащий цикл. Вот пример:
iSum = 0 iProduct =1 for i in range(5): print i if i%2==0: # we aren't interested in the even numbers, skip those print "even" continue else: print "odd" print "calculating" iSum = iSum + i iProduct = iProduct * i print "=========" print iSum print iProduct
Это выводит:
0 even 1 odd calculating 2 even 3 odd calculating 4 even ========= 4 3
Для петель За кулисами
Так что же такое итерабельность? Ну, короткий ответ таков: все, что имеет функцию __iter__
. Функция __iter__
– это одна из тех специальных функций, на которые опирается Python. Введите этот материал в командной строке python и посмотрите, что произойдет:
'__iter__' in dir([1,2,3]) # returns True '__iter__' in dir((1,2,3)) # returns True '__iter__' in dir(range(3)) # returns True '__iter__' in dir(3) # returns False
Итак 3
, целое число, не является итеративным. Попробуйте это:
for i in 3: print i
Это дает нам ошибку:
TypeError: 'int' object is not iterable
Хорошо. Так что __это__
важно. Что он делает? Ну, он возвращает итератор.
Вот действительно простой пример цикла, который мы будем использовать для остальной части этого обсуждения:
my_list = [1,2,3] for i in my_list: print i
Вот что он выводит:
1 2 3
Так что это было довольно просто. Давайте посмотрим, сможем ли мы получить тот же результат без использования цикла for (предполагая, что мы заранее не знаем, что находится в my_list). Вот тут-то и появляются итераторы.
my_iterator = my_list.__iter__()
my_iterator
– это объект listiterator
. Это звучит довольно причудливо, но не пугайтесь. Скоро вы сможете создавать свои собственные итераторы.
Теперь my_iterator
имеет ряд функций. Тот, который нас интересует, называется next
. Попробуйте это:
while True: print my_iterator.next()
Это печатает что-то вроде этого:
1 2 3 Traceback (most recent call last): File "", line 2, in StopIteration
А это почти то, чего мы хотим. Но теперь my_iterator
кажется сломан: Он достиг конца списка и не может идти дальше.
Вот небольшое резюме того, что мы узнали до сих пор:
- Для циклов петля над итерациями.
- итерабли имеют
__iter__
функции __iter__
функции возвращают итераторы- Итераторы используют метод
next
для перемещения от элемента к элементу в пределах связанной с ними итерации - как только у итератора заканчиваются вещи для возврата из функции
next
, он вызывает исключениеStopIteration
всякий раз, когда вызывается next
Теперь давайте сделаем функцию, которая работает точно так же, как цикл for в начале этого раздела:
def my_for_loop(some_iterable): oIter = some_iterable.__iter__() while True: try: print oIter.next() except StopIteration: break
И давайте попробуем:
my_for_loop([1,2,3])
Это выводит:
1 2 3
Как раз то, что мы хотели. Поиграйте с ним немного, посмотрите, что произойдет, если вы передадите итерабли из вводных примеров этого урока.
Использование __iter__ для создания итераторов из Ваших объектов
И, наконец, крутая штука. Теперь мы сделаем ваши собственные объекты итеративными. Я собираюсь подробно описать подход, который я обычно использую. Я использую его, потому что он прост и лаконичен. Я использую один и тот же класс как для итератора, так и для итерируемого. Это полезно, если вам нужен только один итератор для вашего класса (что происходит большую часть времени)
class FibIterable: """ this class is a generates a well known sequence of numbers """ def __init__(self,iLast=1,iSecondLast=0,iMax=50): self.iLast = iLast self.iSecondLast = iSecondLast self.iMax = iMax #cutoff def __iter__(self): return self # because the object is both the iterable and the itorator def next(self): iNext = self.iLast + self.iSecondLast if iNext > self.iMax: raise StopIteration() self.iSecondLast = self.iLast self.iLast = iNext return iNext o = FibIterable() for i in o: print(i)
Это отпечатки пальцев:
1 2 3 5 8 13 21 34
- Итак, чтобы суммировать то, что здесь произошло, мы инициализировали объект
o
классаFilterable
. - Цикл for вызвал
o.__meter__
который только что вернулon
- Для каждой итерации цикла for вызывается цикл
o.next ()
, который вычисляет следующее значение в последовательности - если следующее значение было слишком высоким, то следующее вызывало
стопитерацию
, чтобы вырваться из цикла - в противном случае измененное состояние
o
сохранялось и возвращалось правильное следующее значение.
У этого подхода есть несколько плюсов, о которых я уже упоминал: он лаконичен и прост. Но есть случаи, когда вам могут понадобиться два итератора для одних и тех же данных. Если, следуя сверху, мы снова запустим код:
for i in o: print(i)
Ничего не будет напечатано. Мы должны были бы повторно инициализировать класс, установив o.last
и o.i Second Last
их исходные значения. Хотя это явная слабость, решение тривиально и оставлено как упражнение.
Вывод
Молодец, что забрался так далеко. Знание всех тонкостей циклов и итераторов for значительно упрощает работу в Python. Теперь вы должны быть в таком положении, чтобы действительно получить цикл for
и его внутренние механизмы. Вы можете писать циклы for для итерации по многим видам встроенных в Python итераблей, а если этого недостаточно, вы даже можете создавать свои собственные итерабли.
Если ваш код очень зациклен и имеет дело с большим количеством коллекций, то эти навыки будут незаменимы, и я бы предложил продолжить ваше образование, изучив генераторы Python, понимание списков и механизмы сортировки. Также должен представлять интерес модуль itertools
. С другой стороны, если ваш код имеет тенденцию включать структуры и алгоритмы древовидного или графового типа, я бы предложил изучить рекурсию. Элегантная рекурсивная функция может заменить множество строк вложенных циклов.