Python程序设计基础教程函数定义习题答案与常见报错修正

江岸
江岸
发布时间:2026-11-15 | 4人浏览过
原创

针对Python程序设计基础教程中的函数定义章节,精选典型课后习题提供标准解答与逻辑拆解,并深度剖析修正SyntaxError、TypeError、UnboundLocalError等高频报错及默认参数陷阱。

函数是 Python 程序中组织代码、实现逻辑复用与构建大型程序的核心基础。本文针对《Python程序设计基础教程》中“函数定义”章节的课后习题提供规范的标准参考实现与逻辑推导,并系统梳理新手阶段高频发生的 SyntaxError、TypeError、UnboundLocalError 等报错成因与具体修复方案。

一、函数定义核心概念与参数传递机制

在 Python 中,函数使用 def 关键字定义。函数体内的语句直到该函数被调用时才会执行。很多初学者在编写函数时常把“参数传递”与“全局变量赋值”混淆。理解 Python 的函数执行模型,关键在于掌握对象引用传递(Pass by Object Reference)。

当实参传递给形参时,传递的是该对象在内存中的地址引用,而不是对象本身的深拷贝:

  • 不可变类型(int, float, str, tuple):在函数内部尝试重新赋值时,由于对象本身不可变,Python 会在局部作用域创建新对象并改变形参绑定,外部变量的值不受影响。
  • 可变类型(list, dict, set):如果通过形参调用其就地修改方法(如 append()、pop()、update()),由于形参和实参指向堆内存中的同一个对象,外部变量所持有的内容会同步改变。
Python程序设计基础教程函数定义习题答案与常见报错修正
图 1:Python 函数实参与形参在堆栈中的内存引用映射关系

二、典型函数习题解析与标准代码实现

教程中的函数习题通常涵盖数学运算、容器处理、变长参数以及基础递归。以下选取四类最具考核价值的标准习题进行详细解答。

习题 1:素数判定与区间素数统计

题目要求:编写函数 is_prime(n),接收一个正整数 n,若是素数返回 True,否则返回 False。再编写函数 count_primes(start, end),统计区间 [start, end] 内所有素数的个数并返回其列表。

def is_prime(n):
    """判断正整数 n 是否为素数"""
    if n <= 1:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    # 只需检查至平方根即可,步长为 2 排除偶数
    limit = int(n ** 0.5) + 1
    for i in range(3, limit, 2):
        if n % i == 0:
            return False
    return True

def count_primes(start, end):
    """统计 [start, end] 区间内的所有素数"""
    if start > end:
        return []
    primes = []
    for num in range(start, end + 1):
        if is_prime(num):
            primes.append(num)
    return primes

# 示例调用与验证
res = count_primes(10, 30)
print(f"10到30之间的素数列表: {res}")
print(f"素数总数: {len(res)}")
# 示例输出:
# 10到30之间的素数列表: [11, 13, 17, 19, 23, 29]
# 素数总数: 6

习题 2:序列数据保序去重与频次统计

题目要求:编写函数 deduplicate_keep_order(items),接收一个包含重复元素的列表,返回一个去重后的新列表,且必须保留元素在原列表中首次出现的相对次序。不得直接使用 list(set(items)),因为集合是无序的。

def deduplicate_keep_order(items):
    """
    对列表进行保序去重
    使用辅助 set 记录已遍历元素,实现 O(1) 的时间查找复杂度
    """
    seen = set()
    result = []
    for item in items:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

# 示例调用
raw_list = [3, 1, 2, 3, 4, 1, 5, 2]
clean_list = deduplicate_keep_order(raw_list)
print(f"原始列表: {raw_list}")
print(f"去重列表: {clean_list}")
# 示例输出:
# 原始列表: [3, 1, 2, 3, 4, 1, 5, 2]
# 去重列表: [3, 1, 2, 4, 5]

习题 3:可变参数应用——通用数值统计计算器

题目要求:编写函数 compute_stats(*args, **kwargs)。支持传入任意数量的数值参数 *args;通过关键字参数 round_digits=N 控制保留的小数位数(默认为 2 位),通过 drop_zero=True/False 控制是否过滤掉数值 0。返回包含最大值、最小值、均值和总和的字典。

def compute_stats(*args, **kwargs):
    """
    灵活统计多参数数值特性
    *args: 动态位置参数
    **kwargs: 动态关键字参数
    """
    round_digits = kwargs.get("round_digits", 2)
    drop_zero = kwargs.get("drop_zero", False)

    # 提取所有数值
    numbers = list(args)
    if drop_zero:
        numbers = [x for x in numbers if x != 0]

    if not numbers:
        return {"count": 0, "max": None, "min": None, "sum": 0, "avg": None}

    total_sum = sum(numbers)
    max_val = max(numbers)
    min_val = min(numbers)
    avg_val = round(total_sum / len(numbers), round_digits)

    return {
        "count": len(numbers),
        "max": max_val,
        "min": min_val,
        "sum": round(total_sum, round_digits),
        "avg": avg_val
    }

# 示例调用
stats = compute_stats(12, 0, 45, 78, 0, 99, round_digits=3, drop_zero=True)
print(stats)
# 示例输出:
# {'count': 4, 'max': 99, 'min': 12, 'sum': 234, 'avg': 58.5}

习题 4:递归函数设计——带记忆化防护的斐波那契数列

题目要求:编写递归函数 fibonacci(n) 计算第 n 项斐波那契数(定义:F(1)=1, F(2)=1, F(n)=F(n-1)+F(n-2))。必须包含基础递归边界与边界保护,避免非法输入或性能崩溃。

def fibonacci(n, memo=None):
    """
    带记忆化缓存机制的斐波那契递归实现
    memo: 用于存储中间计算结果,防止 O(2^n) 爆炸
    """
    if not isinstance(n, int) or n < 1:
        raise ValueError("输入必须为大于等于 1 的正整数")
    
    # 字典惰性初始化,规避可变默认参数缺陷
    if memo is None:
        memo = {1: 1, 2: 1}
    
    if n in memo:
        return memo[n]
    
    # 递归拆解并记录结果
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
    return memo[n]

# 示例调用
print(f"第 10 项: {fibonacci(10)}")
print(f"第 35 项: {fibonacci(35)}")
# 示例输出:
# 第 10 项: 55
# 第 35 项: 9227465
Python程序设计基础教程函数定义习题答案与常见报错修正
图 2:解题与实现自定义函数的标准化四个阶段

三、函数定义与调用中常见报错及排查修正

在编写自定义函数时,以下四类报错最为频繁。通过具体的错误样例对比,能够快速定位问题原因并完成修复。

1. SyntaxError: non-default argument follows default argument

错误表现:在定义函数参数列表时,如果把没有默认值的参数写在了带有默认值的参数后面,Python 解析器会在语法分析阶段直接抛出 SyntaxError。

错误代码示例:
# 错误写法:y 有默认值,但随后的 z 却没有默认值
def calculate_price(x, y=10, z):
    return x + y + z
# SyntaxError: non-default argument follows default argument
修正方法:严格遵守形参排布顺序:无默认值的位置参数必须全部位于有默认值的参数之前。
# 正确写法:所有必填位置参数排在前面
def calculate_price(x, z, y=10):
    return x + y + z

2. TypeError: missing required positional argument 或 'xxx' object is not callable

错误表现:此类错误常见于调用阶段。第一种是调用时传入的必选参数少于函数定义所要求的数量;第二种是局部或全局变量名与函数名冲突,导致函数对象被其他数据类型(例如 int 或 list)覆盖。

常见变量覆盖函数引发 TypeError:
# 错误示例:将变量命名为内置函数名 sum
numbers = [10, 20, 30]
sum = 60  # 这里把内置函数 sum 覆盖成了一个 int 整数

# 稍后尝试调用函数
total = sum(numbers)
# TypeError: 'int' object is not callable
修正方法:绝对不要使用 Python 内置函数名(如 sum、max、min、list、dict、str)作为变量名。若发生冲突,使用 del sum 可以恢复内置定义。

3. UnboundLocalError: local variable referenced before assignment

错误表现:函数内部需要读取一个外部全局变量,并在函数内对其进行修改或自增操作时抛出该错误。

错误代码示例:
counter = 0

def increment():
    # 试图直接自增全局变量
    counter += 1  # 等价于 counter = counter + 1
    return counter

increment()
# UnboundLocalError: local variable 'counter' referenced before assignment

深层原因:Python 在编译函数代码块时,只要发现函数体内存在对 counter 的赋值语句(counter = ...),就会在符号表中将 counter 标记为局部变量。执行到右侧 counter + 1 时,局部作用域中的 counter 尚未初始化绑定,因而报错。

修正方案 A(推荐,保持纯函数解耦):通过参数传入并由 return 返回计算结果。
def increment(count):
    return count + 1

counter = 0
counter = increment(counter)  # counter 结果为 1
修正方案 B(显式声明全局引用):在函数开头使用 global 声明。
def increment():
    global counter
    counter += 1
    return counter

4. NameError: name 'xxx' is not defined

错误表现:执行代码时提示函数名未定义。通常有两个诱因:一是函数调用写在函数定义语句之前(Python 逐行解释执行,不存在变量提升);二是由于拼写错误或把函数定义在了一个从不执行的 if 分支内部。

错误代码示例:
# 错误:在函数定义前就执行调用
result = calculate_tax(5000)

def calculate_tax(income):
    return income * 0.03
# NameError: name 'calculate_tax' is not defined
修正方法:调整执行顺序,确保所有 def 语句在被调用前已由解释器完成读取与加载。推荐将通用函数置于模块上方,或者放置于 if __name__ == '__main__': 结构内部统一调用。
Python程序设计基础教程函数定义习题答案与常见报错修正
图 3:函数开发调试阶段四大经典报错现象、成因及修复方案

四、隐蔽陷阱:可变对象充当默认参数的连环灾难

在所有初学者编写的函数 Bug 中,最经典且最隐蔽的莫过于将空列表 [] 或空字典 {} 直接写在默认参数中。

典型陷阱复现:

def add_item(item, target_list=[]):
    target_list.append(item)
    return target_list

print(add_item("苹果"))    # 预期: ['苹果'],实际输出: ['苹果']
print(add_item("香蕉"))    # 预期: ['香蕉'],实际输出: ['苹果', '香蕉'] ??
print(add_item("橘子"))    # 预期: ['橘子'],实际输出: ['苹果', '香蕉', '橘子'] !!

成因分析:

在 Python 中,def 语句是一个可执行语句。当模块被加载执行到 def add_item(...) 时,Python 会创建一个函数对象,同时将默认参数表达式求值一次并保存在该函数对象的 __defaults__ 属性中。

当随后的多次调用省略了 target_list 实参时,每次调用绑定的都是同一个堆内存中的列表实例!因此对该列表的任何修改都会被永久积累下来,跨调用持续污染。

标准防御模式(None 占位法):

def add_item(item, target_list=None):
    """
    使用不可变对象 None 作为默认参数占位符
    在函数体内进行惰性实例化
    """
    if target_list is None:
        target_list = []
    target_list.append(item)
    return target_list

# 再次测试验证
print(add_item("苹果"))    # 输出: ['苹果']
print(add_item("香蕉"))    # 输出: ['香蕉']
print(add_item("橘子"))    # 输出: ['橘子']

五、编写健壮函数的自查清单与最佳实践

在完成基础教程各章节的课后作业时,建议读者在提交代码前依照以下清单逐项自检:

检查维度 自查规范要点 常见反模式与风险
返回值设计 明确区分 print() 打印与 return 返回。绝大多数算法函数应通过 return 产出计算结果供外部进一步处理。 函数体内只有 print(),外部变量赋值得到 None,导致后续运算崩溃。
默认参数 默认参数只能使用 None、int、float、str、bool 等不可变字面量。 直接使用 def f(data=[]),引发跨调用数据串扰。
作用域规范 优先设计纯函数(输入确定则输出确定,不依赖且不改变外部全局状态)。 滥用 global 变量,导致函数产生难以复现的副作用。
防御性校验 在函数入口处对入参的边界值、空数据及类型进行快速断言与拦截。 直接遍历未判空的序列,当传入空值时抛出 IndexError。
Python程序设计基础教程函数定义习题答案与常见报错修正
图 4:Python 函数高质量编码与避坑全景自查脑图
相关专题:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表