Возвращаемые значения функций и области видимости в Python

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

Введение

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

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

Изучение функций без возвращаемых значений

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

Сначала найдите файл no_return_function.py в проводнике файлов (file explorer) в левой части WebIDE. Дважды щелкните по нему, чтобы открыть в редакторе.

Добавьте следующий код в файл no_return_function.py:

def greet():
    print("Hello, welcome to the world of functions!")

## Call the function
greet()

Чтобы запустить этот скрипт, откройте интегрированный терминал, нажав Terminal > New Terminal в верхнем меню. Затем выполните следующую команду:

python ~/project/no_return_function.py

Вы увидите вывод функции, напечатанный в консоли:

Hello, welcome to the world of functions!

Функция greet() выполняет оператор print(), но она не генерирует значение, которое можно сохранить в переменной. Чтобы увидеть, что произойдет, когда вы попытаетесь захватить результат, измените файл no_return_function.py. Замените существующее содержимое следующим кодом:

def greet():
    print("Hello, welcome to the world of functions!")

## Call the function and assign its result to a variable
result = greet()

## Print the value of the result
print(f"The result of calling greet() is: {result}")

Сохраните файл и снова запустите его из терминала:

python ~/project/no_return_function.py

Теперь вывод будет следующим:

Hello, welcome to the world of functions!
The result of calling greet() is: None

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

Добавление возвращаемых значений в функции

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

Откройте файл return_function.py в проводнике файлов (file explorer) в вашем WebIDE.

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

def add_numbers(a, b):
    """This function adds two numbers and returns the sum."""
    sum_result = a + b
    return sum_result

## Call the function and store the returned value
total = add_numbers(5, 3)

## Print the returned value
print(f"The sum is: {total}")

## Use the returned value in another operation
another_total = add_numbers(10, 20) * 2
print(f"Another calculated value: {another_total}")

Сохраните файл и запустите скрипт из терминала:

python ~/project/return_function.py

Вы должны увидеть следующий вывод, демонстрирующий, что возвращенные значения были успешно захвачены и использованы:

The sum is: 8
Another calculated value: 60

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

Добавьте следующий код в конец вашего файла return_function.py, чтобы увидеть это в действии:

def get_user_info():
    """This function returns user information as a tuple."""
    name = "labex"
    age = 25
    city = "Virtual City"
    return name, age, city

## Call the function and unpack the returned tuple into separate variables
user_name, user_age, user_city = get_user_info()

## Print the unpacked values
print(f"Name: {user_name}, Age: {user_age}, City: {user_city}")

Сохраните файл и запустите его снова:

python ~/project/return_function.py

Полный вывод теперь будет выглядеть следующим образом:

The sum is: 8
Another calculated value: 60
Name: labex, Age: 25, City: Virtual City

Здесь get_user_info() возвращает три значения, упакованные в кортеж. Вызывающий код затем распаковывает этот кортеж в три отдельные переменные, что упрощает работу с несколькими возвращаемыми значениями.

Различение локальных и глобальных переменных

Понимание области видимости (scope) переменных имеет решающее значение для написания кода без ошибок. Область видимости определяет, где в вашей программе можно получить доступ к переменной.

  • Глобальная область видимости (Global Scope): Переменные, определенные вне какой-либо функции, являются глобальными. К ним можно получить доступ из любой точки вашего скрипта.
  • Локальная область видимости (Local Scope): Переменные, определенные внутри функции, являются локальными. Доступ к ним возможен только из этой функции.

Давайте исследуем эту концепцию. Откройте файл variable_scope.py в WebIDE.

Добавьте следующий код в файл:

## Global variable
global_variable = "I am a global variable"

def my_function():
    ## Local variable
    local_variable = "I am a local variable"
    print(f"Inside the function:")
    print(f"  Accessing global_variable: {global_variable}")
    print(f"  Accessing local_variable: {local_variable}")

## Call the function
my_function()

## Try to access variables outside the function
print(f"\nOutside the function:")
print(f"  Accessing global_variable: {global_variable}")

## Attempting to access local_variable here would cause a NameError
## print(f"  Accessing local_variable: {local_variable}")

Сохраните файл и запустите его из терминала:

python ~/project/variable_scope.py

Вывод демонстрирует доступность каждой переменной:

Inside the function:
  Accessing global_variable: I am a global variable
  Accessing local_variable: I am a local variable

Outside the function:
  Accessing global_variable: I am a global variable

К глобальной переменной можно получить доступ отовсюду, но локальная переменная ограничена своей функцией. Если бы вы раскомментировали последнюю строку, скрипт завершился бы с ошибкой NameError.

Теперь, что произойдет, если локальная переменная имеет то же имя, что и глобальная? Локальная переменная "затеняет" (shadows) глобальную, что означает, что внутри функции это имя будет ссылаться на локальную переменную.

Добавьте следующий код в конец вашего файла variable_scope.py, чтобы наблюдать этот эффект затенения:

## Global variable
my_variable = "I am the global version"

def another_function():
    ## This local variable shadows the global one
    my_variable = "I am the local version"
    print(f"\nInside another_function(): {my_variable}")

## Call the function
another_function()

## The global variable remains unchanged
print(f"Outside another_function(): {my_variable}")

Сохраните и запустите скрипт снова:

python ~/project/variable_scope.py

Полный вывод теперь будет следующим:

Inside the function:
  Accessing global_variable: I am a global variable
  Accessing local_variable: I am a local variable

Outside the function:
  Accessing global_variable: I am a global variable

Inside another_function(): I am the local version
Outside another_function(): I am the global version

Внутри another_function() my_variable ссылается на локальную версию. Снаружи она ссылается на глобальную версию. Присваивание внутри функции не повлияло на глобальную переменную.

Изменение глобальных переменных с использованием ключевого слова global

По умолчанию вы не можете изменять значение глобальной переменной изнутри функции. Оператор присваивания внутри функции создает новую локальную переменную. Чтобы явно изменить глобальную переменную, необходимо использовать ключевое слово global.

Откройте файл modify_global.py в WebIDE.

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

## Global variable
counter = 0

def increment_counter():
    ## Declare that we intend to modify the global counter
    global counter
    counter += 1
    print(f"Inside function: counter is {counter}")

## Print the initial value
print(f"Before calling function: counter is {counter}")

## Call the function to modify the global variable
increment_counter()

## Print the value after the function call
print(f"After calling function: counter is {counter}")

Сохраните файл и запустите его из терминала:

python ~/project/modify_global.py

Вывод показывает, что глобальная переменная была успешно изменена:

Before calling function: counter is 0
Inside function: counter is 1
After calling function: counter is 1

Оператор global counter сообщает Python, что любая операция с counter внутри этой функции должна затрагивать глобальную переменную, а не создавать новую локальную.

Для сравнения, давайте добавим еще одну функцию, которая не использует ключевое слово global. Добавьте следующий код в конец файла modify_global.py:

def increment_counter_local():
    ## This assignment creates a new local variable named 'counter'
    counter = 100
    print(f"\nInside function (local): counter is {counter}")

## Print the global counter's value before the test
print(f"Before calling function (local test): counter is {counter}")

## Call the function
increment_counter_local()

## The global counter's value is unaffected
print(f"After calling function (local test): counter is {counter}")

Сохраните и запустите скрипт снова:

python ~/project/modify_global.py

Полный вывод наглядно демонстрирует разницу:

Before calling function: counter is 0
Inside function: counter is 1
After calling function: counter is 1
Before calling function (local test): counter is 1
Inside function (local): counter is 100
After calling function (local test): counter is 1

Во втором тесте присваивание counter = 100 затронуло только локальную переменную внутри increment_counter_local(). Глобальный counter сохранил свое значение 1.

Понимание переменных nonlocal во вложенных функциях

Правила области видимости Python также применяются к вложенным функциям (функции, определенной внутри другой функции). Переменная, которая не является локальной для внутренней функции, но является локальной для внешней функции, называется "нелокальной" (nonlocal) переменной.

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

Откройте файл nonlocal_variable.py в WebIDE.

Добавьте следующий код, чтобы продемонстрировать использование nonlocal:

def outer_function():
    outer_variable = "I am in the outer function"

    def inner_function():
        ## Declare that we are modifying the variable from the enclosing scope
        nonlocal outer_variable
        outer_variable = "I have been modified by the inner function"
        print(f"Inside inner_function(): {outer_variable}")

    print(f"Before calling inner_function(): {outer_variable}")
    inner_function()
    print(f"After calling inner_function(): {outer_variable}")

## Call the outer function
outer_function()

Сохраните файл и запустите его из терминала:

python ~/project/nonlocal_variable.py

Вывод показывает, что внутренняя функция успешно изменила переменную внешней функции:

Before calling inner_function(): I am in the outer function
Inside inner_function(): I have been modified by the inner function
After calling inner_function(): I have been modified by the inner function

Оператор nonlocal outer_variable позволяет inner_function перепривязать outer_variable из outer_function.

Теперь посмотрим, что произойдет без ключевого слова nonlocal. Добавьте следующий код в конец файла nonlocal_variable.py:

def outer_function_local_test():
    outer_variable = "I am in the outer function (local test)"

    def inner_function_local_test():
        ## This assignment creates a new local variable
        outer_variable = "I am a local variable in inner_function"
        print(f"\nInside inner_function_local_test(): {outer_variable}")

    print(f"\nBefore calling inner_function_local_test(): {outer_variable}")
    inner_function_local_test()
    print(f"After calling inner_function_local_test(): {outer_variable}")

## Call the outer function for the local test
outer_function_local_test()

Сохраните и запустите скрипт снова:

python ~/project/nonlocal_variable.py

Полный вывод подчеркивает разницу:

Before calling inner_function(): I am in the outer function
Inside inner_function(): I have been modified by the inner function
After calling inner_function(): I have been modified by the inner function

Before calling inner_function_local_test(): I am in the outer function (local test)
Inside inner_function_local_test(): I am a local variable in inner_function
After calling inner_function_local_test(): I am in the outer function (local test)

Во втором примере присваивание внутри inner_function_local_test() создало новую локальную переменную, оставив outer_variable во внешней области видимости без изменений.

Резюме

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

Вы также углубились в области видимости переменных, различая локальные переменные (доступные только внутри функции) и глобальные переменные (доступные во всем скрипте). Вы узнали, как работает затенение переменных (variable shadowing) и как использовать ключевое слово global для изменения глобальной переменной изнутри функции. Наконец, вы рассмотрели вложенные функции и использовали ключевое слово nonlocal для изменения переменных во внешней, неглобальной области видимости.