Рубрики
Без рубрики

Python Beginner Tutorial: для циклов и итераторов

Укрепите свои знания о циклах for и итераторах в Python.

Автор оригинала: Sheena.

Введение и анатомия петли 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 . С другой стороны, если ваш код имеет тенденцию включать структуры и алгоритмы древовидного или графового типа, я бы предложил изучить рекурсию. Элегантная рекурсивная функция может заменить множество строк вложенных циклов.