Share This
Связаться со мной
Крути в низ
Categories
//🐍 Что такое yield в Python? Самый популярный вопрос на Стаковерфлоу по Питону

🐍 Что такое yield в Python? Самый популярный вопрос на Стаковерфлоу по Питону

Самый популярный Python-вопрос на Stackoverflow связан с ключевым словом yield. Разберемся с его назначением и особенностями использования.

chto takoe yield v python samyj populjarnyj vopros na stakoverflou po pitonu 2363bda - 🐍 Что такое yield в Python? Самый популярный вопрос на Стаковерфлоу по Питону

Официальная документация Python содержит достаточно подробное описание всех функции языка и немало примеров. Тем не менее назначение некоторых ключевых слов ставит начинающих разработчиков в тупик. Прежде всего это касается yield – не случайно вопрос о нем остается самым популярным на Stackoverflow.

Вопрос звучит так:

Как используется ключевое слово yield в Python и что именно оно делает? Я пытаюсь понять, как работает, к примеру, этот код:

         def _get_child_candidates(self, distance, min_dist, max_dist):     if self._leftchild and distance - max_dist < self._median:         yield self._leftchild     if self._rightchild and distance + max_dist >= self._median:         yield self._rightchild     

Он вызывается так:

         result, candidates = list(), [self] while candidates:     node = candidates.pop()     distance = node._get_dist(obj)     if distance <= max_dist and distance >= min_dist:         result.extend(node._values)         candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))         return result     

Что происходит в момент вызова метода _get_child_candidates? Что он возвращает – список или элемент? Вызывается ли метод повторно, и когда прекращаются последующие вызовы?

А вот и лучший ответ на вопрос о yield в Python:

Для понимания того, что делает yield, необходимо четко представлять, как работают генераторы и итераторы.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» Интересно, перейти к каналу

Итераторы

Когда вы создаете список, входящие в него элементы можно перебирать один за другим – это и есть итерация:

         >>> mylist = [1, 2, 3] >>> for i in mylist : ...    print(i) 1 2 3      

Список mylist – итерируемый объект. Итератор создается во время генерации списка с помощью спискового включения:

         >>> mylist = [x*x for x in range(3)] >>> for i in mylist : ...    print(i) 0 1 4      

Любые объекты, для которых можно использовать цикл for, являются итерируемыми – списки, строки, файлы. Итерируемые объекты очень удобны, потому что они не ограничивают количество повторных считываний данных. Однако вся информация находится в оперативной памяти, и при большом объеме данных это нежелательно.

Генераторы

Генераторы также относятся к итерируемым объектам, однако данные из них можно считать только один раз. Генераторы не хранят значения в памяти, а создают их на лету:

         >>> mygenerator = (x*x for x in range(3)) >>> for i in mygenerator: ...    print(i) 0 1 4      

Генераторы используются так же, как и списковые включения, отличие заключается в применении круглых скобок () вместо квадратных []. Кроме того, в отличие от списка, сгенерированного с помощью спискового включения, к генератору нельзя обратиться повторно – вычисляя каждый последующий элемент, генератор «забывает» о предыдущем.

yield

Ключевое слово yield используется в функциях так же, как и return – для возвращения результата работы. Разница заключается в том, что yield возвращает генератор.

         >>> def create_generator(): ...    mylist = range(3) ...    for i in mylist: ...        yield i*i ... >>> mygenerator = create_generator() # create a generator >>> print(mygenerator) # mygenerator is an object! <generator object create_generator at 0xb7555c34> >>> for i in mygenerator: ...     print(i) 0 1 4      

Использовать yield вместо return стоит в тех случаях, когда функция возвращает большой объем данных, которые достаточно прочитать один раз.

Для эффективного использования нужно понимать главную особенность yield: при вызове функции код в теле функции не исполняется. Функция просто возвращает объект-генератор. Код вызывается каждый раз, когда for обращается к генератору. При первом запуске функции она будет исполняться, пока не дойдет до yield, после чего вернет первое значение из цикла. При каждом последующем вызове будет происходить следующая итерация и возвращение значения цикла. Процесс будет повторяться, пока генератор не окажется пустым. Генератор считается пустым, если функция не встречает yield – это происходит либо в конце цикла, либо при невыполнении условий if и else.

Пояснение кода, приведенного в вопросе

Генератор:

         # Создание метода узла, который будет возвращать генератор def _get_child_candidates(self, distance, min_dist, max_dist):   # Код будет вызываться при каждом обращении к объекту-генератору   # Если слева от узла есть потомок   # И расстояние соответствует условию, yield вернет этого потомка     if self._leftchild and distance - max_dist < self._median:         yield self._leftchild     # Если есть потомок справа от узла   # И расстояние соответствует условию, yield вернет этого потомка     if self._rightchild and distance + max_dist >= self._median:         yield self._rightchild   # Если функция дошла до этого места, генератор считается пустым      

Вызов:

         # Создание пустого списка и списка со ссылкой на текущий объект result, candidates = list(), [self] # Перебор кандидатов в цикле (в начале там только один элемент) while candidates:     # Удаление последнего кандидата из списка     node = candidates.pop()     # Вычисление расстояния между объектом и кандидатом     distance = node._get_dist(obj)       # Если расстояние соответствует условию, добавляем в результат     if distance <= max_dist and distance >= min_dist:         result.extend(node._values)       # Добавляем потомков кандидата в список,     # чтобы цикл работал до тех пор,     # пока не обойдет всех потомков потомков кандидата     candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) return result      

Как работает код

Цикл проводит итерацию списка, при этом список расширяется во время перебора. Это быстрый способ обхода сгруппированных значений, хотя существует небольшая опасность превращения цикла в бесконечный. В таком случае candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) завершит использование всех значений генератора, но цикл while не остановится на этом, а продолжит процесс создания новых объектов-генераторов, поставляющих значения, отличающиеся от предыдущих (потому что они относятся к другим узлам).

В коде используется метод extend(), который принимает итерируемые объекты и добавляет их к списку. Метод extend() обычно используется для добавления в список другого списка:

         >>> a = [1, 2] >>> b = [3, 4] >>> a.extend(b) >>> print(a) [1, 2, 3, 4]      

Однако в рассматриваемом коде extend() принимает не список, а генератор, что значительно оптимизирует программу:

  • отпадает необходимость повторного считывания данных;
  • не нужно хранить в памяти множество потомков.

Метод extend() может добавлять в список любые итерируемые объекты – генераторы, строки, кортежи, списки. Это называется утиной типизацией.

Где еще пригодится yield

Использование yield решает проблему перегрузки памяти при работе с объемными файлами. К примеру, считывание объемных csv-файлов часто приводит к зависанию и прерыванию программы с ошибкой MemoryError. Чтобы не загружать в память весь файл сразу, и считывать только нужные строки, применяется yield. Этот код, к примеру, вернет количество строк в файле:

         def csv_reader(file_name):     for row in open(file_name, "r"):         yield row     

С помощью yield можно сгенерировать бесконечную (в отличие от range) последовательность чисел:

         def infinite_sequence():     num = 0     while True:         yield num         num += 1     

Выполнение этого кода будет продолжаться до ручного прерывания.

Заключение

В отличие от return, который отправляет вызывающей стороне определенное значение, yield может создавать последовательность значений. Использование yield целесообразно в тех случаях, когда нужно выполнить итерацию по последовательности значений, но при этом хранить всю последовательность в памяти нежелательно.

Yield используются в генераторах Python. Функция-генератор определяется как обычная функция, но всякий раз, когда ей нужно выдать значение, она делает это с помощью ключевого слова yield, а не return. Если тело def содержит yield, функция автоматически становится генераторной.

***

Материалы по теме

  • Как в Python применяются вложенные функции
  • Упрощаем разработку: асинхронные функции Python
  • Как не быть тем парнем, а писать функции лучше

  • 0 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован.

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Свежие комментарии

    Рубрики

    About Author 01.

    blank
    Roman Spiridonov

    Моя специальность - Back-end Developer, Software Engineer Python. Мне 39 лет, я работаю в области информационных технологий более 5 лет. Опыт программирования на Python более 3 лет. На Django более 2 лет.

    Categories 05.

    © Speccy 2022 / All rights reserved

    Связаться со мной
    Close