Python пояснення роботи yield, ітераторів і генераторів

Основне джерело для цього поста - питання і відповіді на stackoverflow.

Для розуміння що робить "yield", ви повинні розуміти що таке генератори. Для розуміння що таке генератори - повинні знати про Ітератор і ітеріруемих об'єктах.

Ітеріруемие об'єкти (iterables)

Хочеться назвати їх неправильним, з точки зору російської мови, словом "ітерабельние" - тобто ті, за якими може відбуватися ітерація. Але, правильніше буде назвати їх "ітеріруемие", хоча особисто мене це слово злегка плутає.

Коли ви створюєте список (list) ви можете зчитувати його елементи по одному - це називається итерацией.

Lst - ітеріруемий об'єкт (iterable). Коли ви використовуєте спискові висловлювання (list comprehensions), ви створюєте список - ітеріруемий об'єкт:

Будь-яке об'єкт який ви можете використовувати в конструкції "for. In." Є ітерірумим: списки, рядки, файлові об'єкти і т.п. Ітерірумие об'єкти досить зручні тому що ви можете зчитувати з них стільки даних, скільки вам необхідно, але при цьому ви зберігаєте всі значення послідовності в пам'яті і це не завжди прийнятно, особливо якщо ви маєте досить великі послідовності.

Генератори

Генератори - ітеріруемие об'єкти, але, в загальному випадку, ви можете їх використовувати тільки один раз. Це пов'язано з тим, що вони не зберігають всі значення в пам'яті, а генерують значення "на льоту" - в міру запиту:

Код виглядає майже так само, як і в попередньому прикладі, тільки замість квадратних скобочек ( "[<.>] ") Були використані круглі (" (<.>) "). Зауважте, що ви не можете виконати цикл по generator вдруге, оскільки нічого в пам'яті не зберігається, спроба пройтися вдруге буде просто проігноровано, тому що generator викине при першому запиті на отримання наступного значення StopIterationError, однак, ви це не помітите, якщо будете використовувати цикл for, це виняток буде перехоплено і інтерпретовано як кінець циклу). Але вручну це можна перевірити:

next - це метод для отримання наступного значення генератора, якщо ви його використовуєте не в циклі for.

Yield - це ключове слово яке використовується так само, як і слово return. Різниця в тому, що функція при цьому починає повертати генератор замість значення.

В даному випадку, з практичної точки зору, це даремний приклад. Відчутну користь ви отримаєте в ситуації, коли ваша функція повинна буде повертати досить великий обсяг даних, але використовувати їх треба буде тільки один раз.

Для того щоб до кінця освоїти оператор yield, ви повинні знати, що коли ви викликаєте функцію, в тілі якої знаходиться yield, виконання цієї функції не відбувається. Замість виконання, функція поверне об'єкт-генератор. Виглядає це трохи дивно на перший погляд - функція викликана, але код не виконано, але, просто запам'ятайте цей факт. Код буде виконуватися при кожній ітерації - будь то цикл "for <.> in "Або виклик методу .next ().

При першому виконанні коду тіла функції код буде виконаний з початку і до першого стрічного оператора yield. Після цього буде повернута первинна значення і виконання тіла функції знову призупинено. Запит наступного значення у генератора під час ітерації змусить код тіла функції виконуватися далі (з попереднього yield'а), поки не зустрінеться наступний yield. Генератор вважається "закінчилися" в разі якщо при черговому виконанні коду тіла функції не було зустрінуте жодного оператора yield.

Управління виконанням генератора

У деяких випадках така логіка може бути корисна, наприклад, для контролю доступу до ресурсів.

Модуль стандартної бібліотеки python - itertools

Модуль стандартної бібліотеки python itertools містить спеціальні функції для створення і роботи з ітераторами. За допомогою цього модуля ви можете: клонувати итератор, об'єднати в ланцюжок декілька ітераторів, згрупувати значення вкладених списків в один, використовувати версію map / zip на генераторах - imap / izip, на цьому список не закінчується.

Наприклад, давайте обчислимо всі можливі варіанти приходу коней у скачках (завдання з комбінаторики - перестановки без повторів елементів):

Зауважте, що застосування list до генератора вирахує все його значення і створить з них список.

Розуміння внутрішньої механіки ітерації

Ітерація - це процес припускає ітеріруемие об'єкти ( "ітерабельние", тобто реалізують метод "__iter __ ()") і ітератори (реалізують метод "__next __ ()"). Ітеріруемие об'єкти - будь-які об'єкти, по яким може проходити ітерація. Ітератори - об'єкти, які дозволяють вам робити ітерацію по ітеріруемим об'єктів. Йшла Саша по шосе.

Схожі статті