Python 第十一天:函数返回值与 None

目录

  1. 函数返回值基础
  2. return 语句详解
  3. None 的本质与应用
  4. 多返回值与元组解包
  5. return 与 print 的区别
  6. 早期返回(Early Return)模式
  7. 条件返回与三目运算符
  8. 生成器函数的返回值
  9. lambda 表达式与返回值
  10. 实战练习与最佳实践

1. 函数返回值基础

什么是返回值

返回值是函数执行完毕后向调用者提供的结果数据。在 Python 中,使用 return 语句来指定函数的返回值。当一个函数没有 return 语句,或者 return 语句后面没有任何表达式时,函数默认返回 None

# 最简单的返回值示例
def greet():
    return "你好,世界!"

result = greet()
print(result)  # 输出: 你好,世界!

返回值的基本类型

Python 函数可以返回各种类型的数据,包括但不限于:

  • 字符串:返回文本信息
  • 数字:返回计算结果
  • 布尔值:返回判断结果
  • 列表/元组/字典:返回集合数据
  • 对象:返回自定义类的实例
  • None:表示空或无返回值
# 返回不同类型的示例
def return_types_demo():
    return "Hello"           # 字符串
    return 42                 # 数字
    return [1, 2, 3]          # 列表
    return {"name": "Alice"}  # 字典
    return True               # 布尔值
    return None               # None 值

注意:函数一旦执行到 return 语句就会立即结束,后面的代码不会执行。上面的例子中,只有第一个 return 会生效。

函数调用与返回值

函数调用本身是一个表达式,它的值就是函数的返回值。我们可以将返回值赋值给变量,参与运算,或者作为其他函数的参数。

# 基本赋值
def add(a, b):
    return a + b

result = add(3, 5)
print(f"3 + 5 = {result}")  # 输出: 3 + 5 = 8

# 参与运算
double = add(3, 5) * 2
print(f"(3 + 5) * 2 = {double}")  # 输出: (3 + 5) * 2 = 16

# 作为函数参数
print(f"结果是: {add(10, 20)}")  # 输出: 结果是: 30

2. return 语句详解

return 的语法形式

Python 中 return 语句有多种语法形式:

# 形式1: return 表达式
def return_expression():
    return 42

# 形式2: return (表达式) - 括号可省略
def return_with_parens():
    return (3.14)

# 形式3: return - 返回 None
def return_no_value():
    return

# 形式4: 无 return 语句 - 隐式返回 None
def no_return():
    pass

return 与函数终止

return 语句不仅返回值,还会立即终止函数的执行。这意味着在 return 之后的任何代码都不会执行。

def process_and_return():
    print("第一步:处理数据")
    return "完成"
    print("这一步永远不会执行")

result = process_and_return()
# 输出: 第一步:处理数据
print(f"返回值: {result}")  # 输出: 返回值: 完成

在循环中使用 return

在函数内的循环中使用 return 可以提前退出函数,这在搜索场景中非常有用。

def find_even_number(numbers):
    """查找第一个偶数,找到则立即返回"""
    for num in numbers:
        if num % 2 == 0:
            return f"找到偶数: {num}"
    return "未找到偶数"

# 测试
print(find_even_number([1, 3, 5, 8, 9]))  # 输出: 找到偶数: 8
print(find_even_number([1, 3, 5, 7, 9]))  # 输出: 未找到偶数

return 与异常处理

try-except-finally 结构中,return 行为有其特殊性:

def try_return():
    try:
        print("try 块执行")
        return "try 返回值"
    except:
        return "except 返回值"
    finally:
        print("finally 块总是执行")

result = try_return()
# 输出:
# try 块执行
# finally 块总是执行
print(f"返回值: {result}")  # 输出: 返回值: try 返回值

3. None 的本质与应用

什么是 None

None 是 Python 中一个特殊的单例对象,表示"空"或"无值"。它不是 0False 或空字符串,而是一个完全独立的类型 NoneType

# None 的类型
print(type(None))  # 输出: <class 'NoneType'>

# None 是单例对象
a = None
b = None
print(a is b)  # 输出: True(注意:应该用 is 而不是 == 比较 None)

None 与 == / is 的区别

这是一个非常容易出错的点:== 比较的是值相等,而 is 比较的是身份(内存地址)相同。

result = None

# 正确判断 None 的方式
if result is None:
    print("result 是 None")
else:
    print("result 不是 None")

# 不推荐的方式(虽然通常也能工作)
if result == None:
    print("result 等于 None")

# None 与 False 的区别
print(None == False)   # 输出: False
print(None == 0)       # 输出: False
print(None == "")      # 输出: False
print(None == [])      # 输出: False

函数默认返回 None

以下情况函数会返回 None

# 情况1: 完全没有 return 语句
def func1():
    print("执行了一些操作")

# 情况2: return 后面没有表达式
def func2():
    return

# 情况3: 只有 pass 语句
def func3():
    pass

# 验证
print(func1())  # 输出: 执行了一些操作 \n None
print(func2())  # 输出: None
print(func3())  # 输出: None

None 的实际应用场景

场景1:可选参数的默认值

def create_user(name, email=None, phone=None):
    """创建用户,可选参数默认为 None"""
    user = {"name": name}
    if email:
        user["email"] = email
    if phone:
        user["phone"] = phone
    return user

# 使用默认值的例子
user1 = create_user("张三")
print(user1)  # 输出: {'name': '张三'}

user2 = create_user("李四", email="li@example.com")
print(user2)  # 输出: {'name': '李四', 'email': 'li@example.com'}

场景2:表示"没有找到"

def find_by_id(items, target_id):
    """在列表中查找 id 匹配的元素"""
    for item in items:
        if item["id"] == target_id:
            return item
    return None  # 明确返回 None 表示未找到

users = [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
    {"id": 3, "name": "Charlie"}
]

result = find_by_id(users, 5)
if result is None:
    print("未找到该用户")
else:
    print(f"找到: {result}")

场景3:条件赋值

def get_display_name(user):
    """获取显示名称,如果昵称为空则用真名"""
    nickname = user.get("nickname")
    return nickname if nickname else user.get("name", "匿名用户")

user1 = {"name": "张三", "nickname": "小张"}
user2 = {"name": "李四"}

print(get_display_name(user1))  # 输出: 小张
print(get_display_name(user2))  # 输出: 李四

4. 多返回值与元组解包

返回多个值

Python 函数可以通过返回元组的方式返回多个值:

def get_statistics(numbers):
    """返回多个统计值"""
    total = sum(numbers)
    count = len(numbers)
    average = total / count if count > 0 else 0
    return total, count, average  # 实际上是返回一个元组

# 调用并接收返回值
result = get_statistics([10, 20, 30, 40, 50])
print(result)  # 输出: (150, 5, 30.0)
print(type(result))  # 输出: <class 'tuple'>

元组解包接收多返回值

Python 支持元组解包,可以将返回的元组直接分解为多个变量:

def get_min_max(numbers):
    """返回最小值和最大值"""
    if not numbers:
        return None, None
    return min(numbers), max(numbers)

# 方式1:元组整体接收
result = get_min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(f"返回的元组: {result}")

# 方式2:元组解包
min_val, max_val = get_min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(f"最小值: {min_val}, 最大值: {max_val}")

# 方式3:使用 _ 忽略不需要的值
_, _, average = get_statistics([10, 20, 30])
print(f"平均值: {average}")

多返回值的高级用法

交换两个变量的值

def swap(a, b):
    """交换两个值"""
    return b, a

x, y = 10, 20
print(f"交换前: x = {x}, y = {y}")
x, y = swap(x, y)
print(f"交换后: x = {x}, y = {y}")
# 输出:
# 交换前: x = 10, y = 20
# 交换后: x = 20, y = 10

返回命名数据

from dataclasses import dataclass

@dataclass
class Result:
    success: bool
    message: str
    data: any = None

def divide(a, b):
    """除法运算,返回命名结果"""
    if b == 0:
        return Result(success=False, message="除数不能为零")
    return Result(success=True, message="成功", data=a / b)

result = divide(10, 3)
if result.success:
    print(f"计算结果: {result.data}")
else:
    print(f"错误: {result.message}")

5. return 与 print 的区别

核心区别

这是一个新手常犯的错误:print 只是将信息输出到控制台,不返回任何有用的值;而 return 是将值返回给调用者。

# 错误示例:混淆 print 和 return
def add_and_print(a, b):
    print(a + b)  # 只是打印,没有返回值

def add_and_return(a, b):
    return a + b  # 返回结果

# print 返回 None
result = add_and_print(3, 5)
print(f"print 的返回值: {result}")  # 输出: print 的返回值: None

# return 返回计算结果
result = add_and_return(3, 5)
print(f"return 的返回值: {result}")  # 输出: return 的返回值: 8

实际应用对比

def calculate_with_debug(numbers):
    """计算时打印中间结果用于调试"""
    total = 0
    for i, num in enumerate(numbers):
        total += num
        print(f"第 {i+1} 步: 加 {num}, 当前总和 = {total}")
    return total

result = calculate_with_debug([1, 2, 3, 4, 5])
print(f"最终结果: {result}")
# 输出:
# 第 1 步: 加 1, 当前总和 = 1
# 第 2 步: 加 2, 当前总和 = 3
# 第 3 步: 加 3, 当前总和 = 6
# 第 4 步: 加 4, 当前总和 = 10
# 第 5 步: 加 5, 当前总和 = 15
# 最终结果: 15

return 用于返回结果

def calculate_total(numbers):
    """真正返回计算结果"""
    total = sum(numbers)
    return total

# 返回值可以用于后续计算
result = calculate_total([1, 2, 3, 4, 5])
print(f"结果可以参与后续计算: {result * 2}")  # 输出: 30

日志记录与返回值的最佳实践

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def process_data(data):
    """演示:正确区分日志和返回值"""
    logger.info(f"开始处理数据: {data}")
    
    if not data:
        logger.warning("数据为空")
        return None
    
    # 处理逻辑...
    result = data.upper()
    
    logger.info(f"处理完成: {data} -> {result}")
    return result

# 使用
output = process_data("hello world")
print(f"最终输出: {output}")

6. 早期返回(Early Return)模式

什么是早期返回

早期返回是指在函数的开头或早期阶段检查条件,如果不满足条件则立即返回,而不是继续执行后续代码。这是一种防御性编程技巧。

验证参数模式

def divide(a, b):
    """除法函数,使用早期返回验证参数"""
    # 早期返回:检查参数有效性
    if b == 0:
        print("错误:除数不能为零")
        return None
    
    # 早期返回:检查类型
    if not isinstance(a, (int, float)):
        print("错误:被除数必须是数字")
        return None
    
    if not isinstance(b, (int, float)):
        print("错误:除数必须是数字")
        return None
    
    # 主逻辑
    return a / b

# 测试
print(divide(10, 2))    # 输出: 5.0
print(divide(10, 0))   # 输出: 错误:除数不能为零 \n None
print(divide("abc", 2)) # 输出: 错误:被除数必须是数字 \n None

减少嵌套的早期返回

# 不使用早期返回(深层嵌套)
def process_order_bad(order):
    if order:
        if order.is_valid():
            if order.has_items():
                # 终于到达主逻辑
                return process(order)
            else:
                return {"error": "订单没有商品"}
        else:
            return {"error": "订单无效"}
    else:
        return {"error": "订单为空"}

# 使用早期返回(扁平结构)
def process_order_good(order):
    """使用早期返回减少嵌套"""
    if not order:
        return {"error": "订单为空"}
    
    if not order.is_valid():
        return {"error": "订单无效"}
    
    if not order.has_items():
        return {"error": "订单没有商品"}
    
    # 主逻辑在最外层
    return process(order)

早期返回与资源清理

import sqlite3

def get_user_by_id(user_id):
    """查询用户,使用早期返回处理各种情况"""
    # 参数验证
    if user_id is None:
        print("错误:user_id 不能为 None")
        return None
    
    if user_id <= 0:
        print("错误:user_id 必须是正整数")
        return None
    
    # 连接数据库
    conn = None
    cursor = None
    
    try:
        conn = sqlite3.connect("users.db")
        cursor = conn.cursor()
        
        # 执行查询
        cursor.execute("SELECT id, name, email FROM users WHERE id = ?", (user_id,))
        row = cursor.fetchone()
        
        # 早期返回:未找到
        if not row:
            print(f"未找到 id 为 {user_id} 的用户")
            return None
        
        # 找到结果
        return {"id": row[0], "name": row[1], "email": row[2]}
        
    except sqlite3.Error as e:
        print(f"数据库错误: {e}")
        return None
    
    finally:
        # 清理资源
        if cursor:
            cursor.close()
        if conn:
            conn.close()

7. 条件返回与三目运算符

if-else 分支返回

def get_grade(score):
    """根据分数返回等级"""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

# 测试
print(get_grade(95))  # A
print(get_grade(82))  # B
print(get_grade(55))  # F

三目运算符(条件表达式)

Python 的三目运算符语法:value_if_true if condition else value_if_false

# 基本用法
age = 20
status = "成年" if age >= 18 else "未成年"
print(status)  # 输出: 成年

# 三目运算符与返回值
def get_max(a, b):
    return a if a > b else b

print(get_max(10, 20))  # 输出: 20

# 嵌套三目运算符(谨慎使用)
def grade(score):
    return "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "D" if score >= 60 else "F"

# 更清晰的写法
def grade_clear(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

返回布尔值

直接返回比较表达式的结果是常见模式:

def is_adult(age):
    """判断是否成年"""
    return age >= 18

def is_even(number):
    """判断是否为偶数"""
    return number % 2 == 0

def is_palindrome(text):
    """判断是否为回文"""
    return text == text[::-1]

# 测试
print(is_adult(20))    # True
print(is_even(7))      # False
print(is_palindrome("上海自来水来自海上"))  # True

8. 生成器函数的返回值

生成器函数基础

生成器函数使用 yield 语句返回值,而不是 return。但生成器函数也可以有 return 语句。

def count_up_to(n):
    """生成器:计数到 n"""
    count = 1
    while count <= n:
        yield count
        count += 1

# 创建生成器对象
generator = count_up_to(5)
print(generator)  # 输出: <generator object count_up_to at 0x...>

# 迭代获取值
for num in count_up_to(5):
    print(num, end=" ")
# 输出: 1 2 3 4 5

生成器的 return 值

当生成器耗尽时,return 语句的值会成为 StopIteration 异常的 value 属性:

def fibonacci(n):
    """生成斐波那契数列的前 n 个数"""
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b
    return "生成完成"  # 这会在 StopIteration 中

gen = fibonacci(10)
try:
    while True:
        print(next(gen), end=" ")
except StopIteration as e:
    print(f"\n生成器结束: {e.value}")
# 输出: 0 1 1 2 3 5 8 13 21 34 
# 生成器结束: 生成完成

yield from 与返回值

def chain_generators():
    """yield from 会把 return 值传递出去"""
    def gen1():
        yield from [1, 2, 3]
        return "gen1 完成"
    
    def gen2():
        yield from [4, 5, 6]
        return "gen2 完成"
    
    def main_gen():
        result1 = yield from gen1()
        print(f"  {result1}")
        result2 = yield from gen2()
        print(f"  {result2}")
        return "全部完成"
    
    gen = main_gen()
    try:
        while True:
            next(gen)
    except StopIteration as e:
        print(f"最终返回值: {e.value}")

chain_generators()
# 输出:
# 1 2 3
#   gen1 完成
# 4 5 6
#   gen2 完成
# 最终返回值: 全部完成

9. lambda 表达式与返回值

lambda 的基本语法

lambda 是创建匿名函数的简洁方式,它自动包含 return 语句:

# 普通函数
def add(a, b):
    return a + b

# lambda 版本
add_lambda = lambda a, b: a + b

# 两者等价
print(add(3, 5))          # 8
print(add_lambda(3, 5))   # 8

lambda 的 return 特性

lambda 函数体只能包含一个表达式,该表达式的值会自动返回:

# lambda 自动返回表达式的值
square = lambda x: x ** 2
print(square(5))  # 25

# 等价于
def square_def(x):
    return x ** 2

# lambda 不能有赋值语句
# 下面的写法是错误的:
# bad_lambda = lambda x: y = x ** 2  # SyntaxError

# lambda 不能有多个表达式
# 下面的写法是错误的:
# bad_lambda = lambda x: print(x); return x ** 2  # SyntaxError

lambda 与条件表达式

# 使用条件表达式的 lambda
abs_value = lambda x: x if x >= 0 else -x
print(abs_value(-5))  # 5

# 嵌套 lambda
max_val = lambda a, b: a if a > b else b
min_val = lambda a, b: a if a < b else b

# 使用场景:排序
students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78}
]

# 按分数降序排序
students_sorted = sorted(students, key=lambda s: s["score"], reverse=True)
for s in students_sorted:
    print(f"{s['name']}: {s['score']}")

lambda 与高阶函数

# map - 对每个元素应用 lambda
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, numbers))
print(f"平方: {squares}")  # [1, 4, 9, 16, 25]

# filter - 过滤元素
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"偶数: {evens}")  # [2, 4]

# reduce - 累积计算
from functools import reduce
total = reduce(lambda acc, x: acc + x, numbers, 0)
print(f"总和: {total}")  # 15

# sorted 的 key 函数
words = ["apple", "banana", "cherry", "date"]
by_length = sorted(words, key=lambda w: len(w))
print(f"按长度排序: {by_length}")
# ['date', 'apple', 'cherry', 'banana']

10. 实战练习与最佳实践

综合练习:学生成绩管理系统

"""
学生成绩管理系统 - 综合练习返回值、None 和多返回值
"""

class Student:
    def __init__(self, student_id, name):
        self.student_id = student_id
        self.name = name
        self.scores = {}
    
    def add_score(self, subject, score):
        """添加科目成绩"""
        if not isinstance(score, (int, float)):
            print(f"错误:成绩必须是数字")
            return False
        if score < 0 or score > 100:
            print(f"错误:成绩必须在 0-100 之间")
            return False
        self.scores[subject] = score
        return True
    
    def get_score(self, subject):
        """获取单科成绩,不存在返回 None"""
        return self.scores.get(subject)
    
    def get_average(self):
        """计算平均分,无成绩时返回 None"""
        if not self.scores:
            return None
        return sum(self.scores.values()) / len(self.scores)
    
    def get_summary(self):
        """返回学生成绩摘要(元组格式)"""
        if not self.scores:
            return (self.student_id, self.name, None, None, 0)
        
        scores = list(self.scores.values())
        return (
            self.student_id,
            self.name,
            min(scores),  # 最低分
            max(scores),  # 最高分
            sum(scores) / len(scores)  # 平均分
        )


def calculate_class_statistics(students):
    """计算班级统计数据"""
    if not students:
        return None
    
    all_scores = []
    for student in students:
        all_scores.extend(student.scores.values())
    
    if not all_scores:
        return None
    
    return {
        "student_count": len(students),
        "total_exams": len(all_scores),
        "class_average": sum(all_scores) / len(all_scores),
        "highest_score": max(all_scores),
        "lowest_score": min(all_scores)
    }


# 测试
if __name__ == "__main__":
    # 创建学生
    s1 = Student("001", "张三")
    s2 = Student("002", "李四")
    s3 = Student("003", "王五")
    
    # 添加成绩
    s1.add_score("数学", 95)
    s1.add_score("语文", 88)
    s1.add_score("英语", 92)
    
    s2.add_score("数学", 78)
    s2.add_score("语文", 85)
    s2.add_score("英语", 82)
    
    s3.add_score("数学", 92)
    s3.add_score("语文", 90)
    s3.add_score("英语", 88)
    
    students = [s1, s2, s3]
    
    # 打印每个学生的成绩摘要
    print("=" * 60)
    print("学生成绩摘要")
    print("=" * 60)
    
    for student in students:
        stu_id, name, min_s, max_s, avg = student.get_summary()
        if avg is not None:
            print(f"{name} ({stu_id}): 最低={min_s}, 最高={max_s}, 平均={avg:.1f}")
        else:
            print(f"{name} ({stu_id}): 暂无成绩")
    
    # 打印班级统计
    stats = calculate_class_statistics(students)
    print("\n" + "=" * 60)
    print("班级统计")
    print("=" * 60)
    if stats:
        print(f"学生人数: {stats['student_count']}")
        print(f"考试总数: {stats['total_exams']}")
        print(f"班级平均分: {stats['class_average']:.1f}")
        print(f"最高分: {stats['highest_score']}")
        print(f"最低分: {stats['lowest_score']}")

最佳实践总结

  1. 明确返回值:函数应该总是有明确的返回值,或者明确返回 None
  2. 避免返回不同类型:尽量保持函数返回类型的一致性
  3. 使用早期返回:减少嵌套,提高代码可读性
  4. 正确区分 print 和 return:print 用于调试和用户输出,return 用于返回数据
  5. 善用 None:使用 None 表示"没有"或"未找到"是 Python 的惯例
  6. 类型注解:为函数添加类型注解使代码更清晰
def find_user(users: list, user_id: int) -> dict | None:
    """
    根据 ID 查找用户
    
    Args:
        users: 用户列表
        user_id: 要查找的用户 ID
    
    Returns:
        找到返回用户字典,未找到返回 None
    """
    for user in users:
        if user.get("id") == user_id:
            return user
    return None

常见错误与避免方法

错误 后果 正确做法
忘记 return 函数返回 None 明确需要返回什么
return 后有代码 代码永不执行 return 放在最后或使用早期返回
用 == 比较 None 不好(虽然通常能工作) 使用 is None
print 当 return 返回 None 使用 return value
返回可变默认值 列表/字典会被共享 使用 None 作为默认值

总结

今天我们学习了 Python 函数返回值和 None 的核心概念:

  1. return 语句:将值返回给调用者,同时终止函数执行
  2. None 类型:表示"空"或"无值",是 NoneType 的唯一实例
  3. 多返回值:通过返回元组实现,可以用元组解包接收
  4. return vs print:前者返回数据,后者仅输出到控制台
  5. 早期返回:减少嵌套,提高代码可读性
  6. 生成器返回值:使用 return 指定迭代结束的值
  7. lambda 表达式:自动返回表达式结果

掌握这些概念对于编写高质量的 Python 代码至关重要。在实际开发中,要养成明确返回值、使用早期返回、正确处理 None 的好习惯。