Как проверить, является ли функция генератором в Python

PythonPythonBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

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

В рамках практического занятия (лабораторной работы) вы научитесь различать функции и генераторы, проверять их типы с помощью inspect.isgeneratorfunction и определять наличие ключевого слова yield. Вы создадите скрипт на Python, чтобы проиллюстрировать различия, наблюдая, как функции возвращают полный результат, в то время как генераторы последовательно выдают значения, приостанавливая и возобновляя выполнение по мере необходимости.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ModulesandPackagesGroup -.-> python/standard_libraries("Common Standard Libraries") python/AdvancedTopicsGroup -.-> python/generators("Generators") subgraph Lab Skills python/function_definition -.-> lab-559519{{"Как проверить, является ли функция генератором в Python"}} python/standard_libraries -.-> lab-559519{{"Как проверить, является ли функция генератором в Python"}} python/generators -.-> lab-559519{{"Как проверить, является ли функция генератором в Python"}} end

Различие между функциями и генераторами

На этом этапе вы узнаете ключевые различия между обычными функциями Python и генераторами. Понимание этого различия является важным для написания эффективного и экономного по памяти кода, особенно при работе с большими наборами данных или бесконечными последовательностями.

Функции:

Функция представляет собой блок кода, выполняющий определенную задачу. Когда функция вызывается, она выполняет свой код, может выполнять вычисления и возвращает значение (или None, если явного оператора return нет). Состояние функции не сохраняется между вызовами.

Генераторы:

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

Покажем это на примере. Сначала создайте файл с именем function_vs_generator.py в каталоге ~/project с помощью редактора VS Code.

## ~/project/function_vs_generator.py

## Regular function
def square_numbers_function(numbers):
    result = []
    for number in numbers:
        result.append(number * number)
    return result

## Generator function
def square_numbers_generator(numbers):
    for number in numbers:
        yield number * number

## Example usage
numbers = [1, 2, 3, 4, 5]

## Using the function
function_result = square_numbers_function(numbers)
print("Function Result:", function_result)

## Using the generator
generator_result = square_numbers_generator(numbers)
print("Generator Result:", list(generator_result)) ## Convert generator to list for printing

Теперь выполните скрипт Python:

python ~/project/function_vs_generator.py

Вы должны увидеть следующий вывод:

Function Result: [1, 4, 9, 16, 25]
Generator Result: [1, 4, 9, 16, 25]

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

Для дальнейшего иллюстрации различия изменим скрипт, чтобы вывести тип возвращаемого объекта:

## ~/project/function_vs_generator.py

## Regular function
def square_numbers_function(numbers):
    result = []
    for number in numbers:
        result.append(number * number)
    return result

## Generator function
def square_numbers_generator(numbers):
    for number in numbers:
        yield number * number

## Example usage
numbers = [1, 2, 3, 4, 5]

## Using the function
function_result = square_numbers_function(numbers)
print("Function Result Type:", type(function_result))

## Using the generator
generator_result = square_numbers_generator(numbers)
print("Generator Result Type:", type(generator_result))

Выполните скрипт еще раз:

python ~/project/function_vs_generator.py

Вывод будет следующим:

Function Result Type: <class 'list'>
Generator Result Type: <class 'generator'>

Это ясно показывает, что функция возвращает list, в то время как генератор возвращает объект generator. Генераторы экономят память, потому что они не хранят все значения в памяти одновременно. Они генерируют значения по запросу.

Проверка типа с помощью inspect.isgeneratorfunction

На предыдущем этапе вы узнали основные различия между функциями и генераторами. Теперь давайте узнаем, как программно определить, является ли функция генераторной функцией, используя метод inspect.isgeneratorfunction(). Это особенно полезно, когда вы работаете с кодом, в котором вы, возможно, не знаете деталей реализации функции.

Модуль inspect в Python предоставляет инструменты для интроспекции (анализа внутренних характеристик объектов, таких как функции, классы, модули и т.д.). Метод inspect.isgeneratorfunction() специально проверяет, является ли заданный объект генераторной функцией.

Давайте изменим файл function_vs_generator.py в каталоге ~/project, чтобы включить эту проверку. Откройте файл в VS Code и добавьте следующий код:

## ~/project/function_vs_generator.py

import inspect

## Regular function
def square_numbers_function(numbers):
    result = []
    for number in numbers:
        result.append(number * number)
    return result

## Generator function
def square_numbers_generator(numbers):
    for number in numbers:
        yield number * number

## Check if it's a generator function
print("Is square_numbers_function a generator function?", inspect.isgeneratorfunction(square_numbers_function))
print("Is square_numbers_generator a generator function?", inspect.isgeneratorfunction(square_numbers_generator))

В этом коде мы сначала импортируем модуль inspect. Затем мы используем inspect.isgeneratorfunction() для проверки как square_numbers_function, так и square_numbers_generator.

Теперь выполните скрипт Python:

python ~/project/function_vs_generator.py

Вы должны увидеть следующий вывод:

Is square_numbers_function a generator function? False
Is square_numbers_generator a generator function? True

Это подтверждает, что inspect.isgeneratorfunction() правильно определяет генераторную функцию.

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

Проверка использования ключевого слова yield

На этом этапе мы сосредоточимся на том, как наличие ключевого слова yield фундаментально определяет генераторную функцию. Функция считается генератором тогда и только тогда, когда она содержит хотя бы одну инструкцию yield. Давайте создадим простой тест, чтобы подтвердить это.

Откройте файл function_vs_generator.py в каталоге ~/project с помощью VS Code. Мы добавим функцию, которая не использует yield, и посмотрим, как она ведет себя, когда рассматривается как потенциальный генератор.

## ~/project/function_vs_generator.py

import inspect

## Regular function
def square_numbers_function(numbers):
    result = []
    for number in numbers:
        result.append(number * number)
    return result

## Generator function
def square_numbers_generator(numbers):
    for number in numbers:
        yield number * number

## Function that returns a list, not a generator
def return_list(numbers):
    return [x for x in numbers]

## Check if it's a generator function
print("Is square_numbers_function a generator function?", inspect.isgeneratorfunction(square_numbers_function))
print("Is square_numbers_generator a generator function?", inspect.isgeneratorfunction(square_numbers_generator))
print("Is return_list a generator function?", inspect.isgeneratorfunction(return_list))

В этом обновленном коде мы добавили функцию return_list, которая просто возвращает список с использованием списочного выражения. Она не содержит ключевого слова yield. Затем мы используем inspect.isgeneratorfunction() для проверки, является ли return_list генераторной функцией.

Теперь выполните скрипт Python:

python ~/project/function_vs_generator.py

Вы должны увидеть следующий вывод:

Is square_numbers_function a generator function? False
Is square_numbers_generator a generator function? True
Is return_list a generator function? False

Как и ожидалось, return_list не определяется как генераторная функция, потому что она не использует ключевое слово yield. Это демонстрирует, что ключевое слово yield является обязательным для определения генератора в Python. Без него функция ведет себя как обычная функция, возвращает значение (или None) и не сохраняет свое состояние между вызовами.

Резюме

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

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