深入建议 50 分钟学习等级 4/8

PYTHON LESSON 058

考点精讲:函数组合拳与变量领地

掌握一次返回多个值、函数之间互相调用的拆题方法,彻底分清局部变量与全局变量。

00 · 学习目标

这一课要解决什么?

先想一想一个函数怎么同时交出两个答案?函数里和函数外的同名变量会打架吗?
完成任务日历天数计算器
学习顺序正式定义 → 机制推演 → 边界与反例 → 迁移

学完后,你应该能够

  • return a, b 一次返回多个值(元组)
  • 函数里调用别的函数,大任务拆小任务
  • 局部变量遮蔽同名全局变量,互不影响

01 · 核心概念

多返回值、函数调用函数、局部与全局作用域的深入辨析

多返回值、函数调用函数、局部与全局作用域的深入辨析:return min(nums), max(nums) 用逗号隔开两个值,Python 会自动把它们打包成元组;调用处写 low, high = ... 两个变量一接,就各自拿到答案。选择题常考“函数返回多个值的本质”——元组。

考级对应GESP Python 4 级 · 函数的定义与调用GESP Python 4 级 · 变量作用域
学习边界

大量从队首删除时 list.pop(0) 是 O(n),应使用 collections.deque。

02 · 语法与规则

先记住这 3 条,再开始写程序

01return a, b 一次返回多个值(元组)

先准确读出这条写法的结构与作用。

02函数里调用别的函数,大任务拆小任务

换一组最小数据,手工推演一次结果。

03局部变量遮蔽同名全局变量,互不影响

再用边界值或反例确认它的适用条件。

03 · 完整实例

代码、运行结果和解释放在一起看

g4-function-advanced.pyPYTHON 3.12
# 考点精讲:函数进阶与变量作用域
# 1. 一次返回多个值
def min_max(nums):
    return min(nums), max(nums)

low, high = min_max([88, 95, 72, 100, 63])
print("最低分:", low, "最高分:", high)

# 2. 函数调用函数:大任务拆小
def is_leap(year):
    return year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)

def days_of_month(year, month):
    if month == 2:
        return 29 if is_leap(year) else 28
    if month in [4, 6, 9, 11]:
        return 30
    return 31

print("2024 年 2 月有", days_of_month(2024, 2), "天")
print("2025 年 2 月有", days_of_month(2025, 2), "天")

# 3. 作用域实验:同名变量各管各
count = 100          # 全局 count

def test():
    count = 1        # 局部 count,和全局的不是同一个
    print("函数里的 count:", count)

test()
print("函数外的 count:", count)

# 4. 传进去的是“同一个列表”
def add_score(board, score):
    board.append(score)

board = [90, 85]
add_score(board, 95)
print("积分表:", board)
运行结果OUTPUT
最低分: 63 最高分: 100
2024 年 2 月有 29 天
2025 年 2 月有 28 天
函数里的 count: 1
函数外的 count: 100
积分表: [90, 85, 95]

return a, b 返回的其实是一个元组,用两个变量接住就会自动拆包。

在新标签运行和修改这个实例已装入本课代码 · 可自定义输入 · 可提交判题

04 · 逐步理解

每一步只解决一个问题

  1. 01

    return 也能一次交两件货

    return min(nums), max(nums) 用逗号隔开两个值,Python 会自动把它们打包成元组;调用处写 low, high = ... 两个变量一接,就各自拿到答案。选择题常考“函数返回多个值的本质”——元组。

    PYTHON
    def min_max(nums):
        return min(nums), max(nums)
  2. 02

    大任务拆成小函数

    算“某年某月有几天”,最难的是 2 月要看闰年。把“是不是闰年”单独写成 is_leap,days_of_month 里直接调用它——每个函数只干一件事,合起来解决大问题。

    PYTHON
    def days_of_month(year, month):
        if month == 2:
            return 29 if is_leap(year) else 28
  3. 03

    闰年判断的逻辑链

    闰年规则:能被 400 整除,或者能被 4 整除但不能被 100 整除。翻译成代码就是 year % 400 == 0 or (year % 4 == 0 and year % 100 != 0),括号保证先算 and 再算 or。

    PYTHON
    return year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)
  4. 04

    同名变量的“遮蔽”现象

    函数里写 count = 1,并没有改动全局的 count,而是在函数内部新造了一个同名局部变量,把全局的暂时“遮住”了。所以函数里打印 1,函数外打印的还是 100。

    PYTHON
    count = 100
    def test():
        count = 1
        print(count)
  5. 05

    读程序题的作用域口诀

    考级读程序题最爱考作用域。口诀:函数里“赋值”就是造局部变量(除非用 global 声明);只是“读取”则会向外找全局变量。拿笔在草稿纸上给每个变量标出领地,就不容易错。

  6. 06

    列表传参为什么例外

    add_score 里 append 居然改动了外面的 board?因为传参传的是“同一个列表”本身,不是复制品。数字、字符串不可变所以没事,列表、字典可变就会这样——这是 4 级必考点。

    PYTHON
    def add_score(board, score):
        board.append(score)

05 · 练习与检验

自己写出来,才算真正学会

本课实作步骤
  1. 1运行程序,看 min_max 一次交出两个答案
  2. 2用 days_of_month 查一查你出生的月份
  3. 3解释为什么函数外的 count 还是 100
  4. 4给 add_score 改成返回新列表的版本
打开本课编程实验室编辑、运行、测试、判题都在一个页面完成

06 · 完整知识

继续理解定义、规则和适用边界

第一次学习先完成上面的六个步骤;需要查定义、核对规则、分析误区或理解“为什么”时,再展开对应知识章。

核心数据结构列表、元组、字典与集合按顺序、可变性、唯一性和查找需求选择容器,并掌握遍历、推导式、排序与复制。

正式定义

容器保存多个对象。list 是可变有序序列,tuple 是不可变有序序列,dict 保存唯一键到值的映射,set 保存无序且不重复的可哈希对象。

必须掌握

  • list 支持下标、切片、append、extend、insert、pop、remove 与 sort;多数修改方法返回 None。
  • tuple 的逗号比括号更关键,单元素元组必须写成 (value,)。
  • dict 保持插入顺序;键必须可哈希且唯一,get 可提供缺省值,items 同时遍历键和值。
  • set 用于去重与集合运算:| 并、& 交、- 差、^ 对称差;空集合必须写 set()。
  • enumerate 同时给出序号和值,zip 并行遍历多个可迭代对象,默认在最短输入处停止。
  • 浅复制只复制最外层容器;嵌套可变对象仍可能共享。

CORE CONTAINER METHODS

四类核心容器方法完整速查

修改型方法多数返回 None;表中“浅复制”不会递归复制内部可变对象。

list、tuple、dict、set 的公开核心方法
类型方法核心作用
listappend(x)末尾加入一个对象
listextend(iterable)末尾加入可迭代对象的每一项
listinsert(i, x)在下标 i 前插入
listremove(x)删除第一个等于 x 的元素,不存在时报错
listpop([i])删除并返回指定项,默认末项
listclear()删除全部元素
listindex(x[, start[, stop]])返回第一个匹配下标
listcount(x)统计等于 x 的元素数
listsort(*, key=None, reverse=False)稳定地原地排序
listreverse()原地逆序
listcopy()浅复制列表
tuplecount(x)统计元素次数
tupleindex(x[, start[, stop]])返回第一个匹配下标
dictclear()删除全部键值对
dictcopy()浅复制字典
dictfromkeys(iterable[, value])用一组键创建新字典
dictget(key[, default])安全取值,缺键返回默认值
dictitems()键值对动态视图
dictkeys()键动态视图
dictpop(key[, default])删除键并返回值
dictpopitem()删除并返回最后加入的键值对
dictsetdefault(key[, default])缺键时写入默认值并返回
dictupdate(other)用映射或键值对更新
dictvalues()值动态视图
setadd(x)加入一个元素
setclear()删除全部元素
setcopy()浅复制集合
setdiscard(x)删除元素,不存在也不报错
setremove(x)删除元素,不存在时报错
setpop()删除并返回任意元素
setunion(*others)返回并集
setupdate(*others)用并集原地更新
setintersection(*others)返回交集
setintersection_update(*others)用交集原地更新
setdifference(*others)返回差集
setdifference_update(*others)用差集原地更新
setsymmetric_difference(other)返回对称差
setsymmetric_difference_update(other)用对称差原地更新
setissubset(other)是否为子集
setissuperset(other)是否为超集
setisdisjoint(other)是否没有共同元素

常见误区

  • 把 list.sort() 的返回值赋回列表
  • 遍历 dict 时同时改变其大小
  • 用可变 list 当字典键
  • 把 set 当成有固定顺序的序列

适用边界

  • 大量从队首删除时 list.pop(0) 是 O(n),应使用 collections.deque。
  • 需要有序映射的特殊操作、计数或默认值时可查阅 collections,但先理解基本 dict。
打开本章完整示例与独立阅读页 →
程序组织函数、参数、返回值与作用域用明确的输入、输出和职责拆分程序,理解参数绑定、作用域、递归与可变对象。

正式定义

函数把一段可复用行为绑定到名字。调用时实参按规则绑定到形参,函数执行后用 return 交回结果;没有显式 return 时返回 None。名字解析遵循局部、闭包、全局、内置的 LEGB 顺序。

必须掌握

  • 位置参数先于关键字参数;默认值在 def 执行时创建一次,不应直接使用可变容器作默认值。
  • return 立即结束当前函数;return a, b 实际返回一个元组。
  • 函数内赋值默认创建局部变量;global 声明模块级名字,nonlocal 声明最近外层函数名字。
  • 传参传递的是对象引用;函数是否影响调用方取决于对象是否可变以及函数是否原地修改。
  • 递归必须有可到达的终止条件,并要考虑调用深度、重复子问题和栈空间。
  • 纯函数更容易测试;把输入输出、计算逻辑和全局状态分开能显著减少错误。

常见误区

  • 可变默认参数在多次调用间共享
  • 忘记 return 导致得到 None
  • 局部变量遮蔽全局同名变量
  • 递归没有缩小问题规模

适用边界

  • lambda 只能包含一个表达式,适合短小的排序键,不适合塞入复杂业务逻辑。
  • 装饰器、生成器和闭包属于函数模型的进阶应用,应在基本参数与作用域稳定后学习。
打开本章完整示例与独立阅读页 →
Python 基础运算符、表达式与优先级完整区分算术、比较、逻辑、成员、身份和位运算,并用优先级表消除歧义。

正式定义

表达式求值得到一个值。运算符规定如何组合操作数;当一个表达式含多个运算符时,优先级和结合方向决定求值顺序,括号可以明确改变顺序。

必须掌握

  • / 总是得到浮点结果;// 是向负无穷方向取整的整除;% 与 // 满足 a == (a // b) * b + a % b。
  • 比较可以链式书写,如 0 <= x < 10;and/or 会短路并返回最后求值的操作数,不一定返回 bool。
  • == 比较值是否相等,is 比较是否为同一个对象;判断 None 应写 is None。
  • in/not in 做成员测试;对 dict 测试的是键。
  • 位运算作用于整数的二进制位;负整数按无限长二进制补码语义理解。
  • 复杂表达式即使能靠优先级正确运行,也应使用括号表达意图。

常见误区

  • 把 // 当成简单截断
  • 用 is 比较数字或字符串的值
  • 忘记 and 的优先级高于 or
  • 连续位移、比较和逻辑运算却不加括号

适用边界

  • 浮点数比较受二进制表示误差影响,需要按问题选择容差。
  • 运算符可由自定义类重载,因此相同符号对不同类型可能有不同语义。
打开本章完整示例与独立阅读页 →
编码与文本字符串、转义、切片与格式化从不可变字符序列到检索、拆分、拼接、格式化和常用判断方法。

正式定义

str 是不可变的 Unicode 字符序列。下标访问单个字符,切片生成新字符串;任何看似“修改字符串”的方法都会返回新对象。

必须掌握

  • 下标从 0 开始,负下标从末尾开始;切片 s[start:stop:step] 不包含 stop,step 不能为 0。
  • 转义序列用于在字面量中表示换行、制表、引号、反斜杠或码点;原始字符串仍有末尾反斜杠限制。
  • find 找不到返回 -1,index 找不到抛 ValueError;count 统计不重叠出现次数。
  • split 把字符串拆成列表,join 用一个字符串连接可迭代对象中的字符串,strip 只删除两端字符。
  • f-string 的格式说明可控制宽度、对齐、精度、进制和百分比;格式化不改变原值。
  • isalpha/isdigit 等按 Unicode 定义,不只识别英文字母和 ASCII 数字。

常见误区

  • 尝试 s[0] = 'A' 原地修改字符串
  • 把 strip('ab') 误解为删除完整子串 'ab'
  • find 返回 -1 后直接拿去当有效下标
  • 把字节长度与字符长度混为一谈

适用边界

  • 正则表达式不在低等级字符串必修范围,但复杂模式匹配时应使用 re,而不是堆叠大量 split/find。
  • 面向用户的字符计数还可能涉及组合字符和字素簇,len 统计的是 Unicode 码点序列长度。
打开本章完整示例与独立阅读页 →

完成检查

确认自己会解释、会编写、会验证