Python 函数的返回值与作用域

PythonBeginner
立即练习

介绍

在这个 Lab 中,你将通过探索函数的返回值和变量作用域(variable scope)的概念来加深对 Python 中函数的理解。你将首先检查那些没有明确返回值的函数,观察它们如何在不产生可供他处使用的结果的情况下执行操作。

随后,你将学习如何向函数添加返回值,使其能够产生可被捕获并在你的程序中使用的输出。接着,这个 Lab 将指导你区分局部变量(local variables)和全局变量(global variables),理解它们各自的作用域以及它们如何影响变量在函数内部和外部的可访问性。你还将学习如何使用 global 关键字在函数内部修改全局变量,并探索嵌套函数中 nonlocal 变量的概念。

探索不带返回值的函数

在这一步,我们将探索那些没有明确 return 语句的函数。这些函数执行一个操作,比如向控制台打印内容,但不会向调用它们的代码部分传回任何值。

首先,在 WebIDE 左侧的文件浏览器中找到文件 no_return_function.py。双击它在编辑器中打开。

将以下代码添加到 no_return_function.py 文件中:

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

## 调用函数
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!")

## 调用函数并将结果赋给一个变量
result = greet()

## 打印 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 语句的函数都会自动返回 NoneNone 是一个特殊值,表示没有值。这证实了此类函数是用于其副作用(如打印)而不是用于产生数据。

为函数添加返回值

与上一步相反,大多数函数的设计目的是计算一个值并将其返回给调用者。这是使用 return 关键字实现的。return 语句会立即退出函数,并将指定的值传回。

在你的 WebIDE 文件浏览器中打开文件 return_function.py

将以下代码添加到文件中,以定义一个将两个数字相加并返回其和的函数:

def add_numbers(a, b):
    """这个函数将两个数字相加并返回它们的和。"""
    sum_result = a + b
    return sum_result

## 调用函数并存储返回的值
total = add_numbers(5, 3)

## 打印返回的值
print(f"The sum is: {total}")

## 在另一个操作中使用返回的值
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 对象,包括数字、字符串、列表,甚至是元组(tuple)。返回一个元组是同时返回多个值的常见方法。

将以下代码添加到 return_function.py 文件的末尾,以查看其实际效果:

def get_user_info():
    """这个函数以元组的形式返回用户信息。"""
    name = "labex"
    age = 25
    city = "Virtual City"
    return name, age, city

## 调用函数并将返回的元组解包(unpack)到单独的变量中
user_name, user_age, user_city = get_user_info()

## 打印解包后的值
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)对于编写无错误的(bug-free)代码至关重要。作用域决定了你的程序中可以在哪里访问一个变量。

  • 全局作用域(Global Scope):在任何函数外部定义的变量是全局的。你的脚本中任何地方都可以访问它们。
  • 局部作用域(Local Scope):在函数内部定义的变量是局部的。它们只能在该函数内部访问。

让我们来探索这个概念。在 WebIDE 中打开 variable_scope.py 文件。

将以下代码添加到文件中:

## 全局变量
global_variable = "I am a global variable"

def my_function():
    ## 局部变量
    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}")

## 调用函数
my_function()

## 尝试在函数外部访问变量
print(f"\nOutside the function:")
print(f"  Accessing global_variable: {global_variable}")

## 尝试在这里访问 local_variable 会导致 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 而失败。

那么,如果一个局部变量与一个全局变量同名会发生什么呢?局部变量会“遮蔽”(shadow)全局变量,这意味着在函数内部,该名称将引用局部变量。

将以下代码添加到你的 variable_scope.py 文件的末尾,以观察这种遮蔽效应:

## 全局变量
my_variable = "I am the global version"

def another_function():
    ## 这个局部变量遮蔽了全局变量
    my_variable = "I am the local version"
    print(f"\nInside another_function(): {my_variable}")

## 调用函数
another_function()

## 全局变量保持不变
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 关键字。

在 WebIDE 中打开 modify_global.py 文件。

添加以下代码,该代码定义了一个全局计数器和一个递增它的函数:

## 全局变量
counter = 0

def increment_counter():
    ## 声明我们打算修改全局 counter
    global counter
    counter += 1
    print(f"Inside function: counter is {counter}")

## 打印初始值
print(f"Before calling function: counter is {counter}")

## 调用函数以修改全局变量
increment_counter()

## 打印函数调用后的值
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():
    ## 此赋值创建了一个名为 'counter' 的新局部变量
    counter = 100
    print(f"\nInside function (local): counter is {counter}")

## 在测试前打印全局 counter 的值
print(f"Before calling function (local test): counter is {counter}")

## 调用函数
increment_counter_local()

## 全局 counter 的值不受影响
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 的工作方式,但它应用于嵌套作用域而不是全局作用域。

在 WebIDE 中打开 nonlocal_variable.py 文件。

添加以下代码以演示 nonlocal 的用法:

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

    def inner_function():
        ## 声明我们正在修改来自包围作用域(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}")

## 调用外部函数
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 重新绑定(rebind)来自 outer_functionouter_variable

现在,让我们看看如果没有 nonlocal 关键字会发生什么。将以下代码添加到 nonlocal_variable.py 的末尾:

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

    def inner_function_local_test():
        ## 此赋值创建了一个新的局部变量
        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}")

## 调用外部函数进行局部测试
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)返回。

你还深入研究了变量作用域,区分了局部变量(只能在函数内部访问)和全局变量(可在整个脚本中访问)。你学习了变量遮蔽(shadowing)的工作原理,以及如何在函数内部使用 global 关键字来修改全局变量。最后,你研究了嵌套函数,并使用 nonlocal 关键字来修改包围作用域(enclosing scope)中非全局的变量。