函数
语法骨架
def 函数名(参数1, 参数2, ...):
"""文档字符串(可选,说明函数作用)"""
函数体
return 返回值 # 可选,没有 return 则返回 None
函数整体的解释
┌──────────────────────────────────────────────────────┐
│ 函数的四要素 │
├──────────────────────────────────────────────────────┤
│ 1. 函数名 → 怎么叫它(见名知意,snake_case) │
│ 2. 参数 → 输入(可无) │
│ 3. 函数体 → 干什么(缩进代码块) │
│ 4. 返回值 → 输出(用 return,可无) │
└──────────────────────────────────────────────────────┘
参数
位置参数和关键字参数
默认参数(缺省参数)
*args和**kwargs(不定长参数)
*args:接收任意多个位置参数
参数解读:
*args— 星号把调用时传进来的多个位置参数收集成一个元组args。my_sum(1,2,3,4)里args=(1,2,3,4)。这样函数就能接收"不确定个数"的参数
**kwargs:接收任意多个关键字参数
参数解读:
**kwargs— 双星号把多个key=value收集成一个字典kwargs。适合"参数名不固定"的场景(如配置项)
调用时的参数解包
需求:已经有了一个列表[1,2,3]和一个字典{"a":1},想直接把它们"拆开"当成参数传给函数,而不是一个个手写。
分析:在调用函数时,*把序列(列表/元组)拆成位置参数,**把字典拆成关键字参数。注意:这和解包赋值(a, b = [1, 2])不是一回事,这里专指"传参时解包"。
步骤:新建代码/day03_unpack_args.py:
递归
需求:有些问题"自己定义自己"更自然,比如"n 的阶乘 = n × (n-1) 的阶乘"。这种"函数内部调用自己"就是递归。
分析:递归要满足三要素,缺一不可:
结束条件(终止条件):最小的情况直接给结果,不再递归,否则无限循环。
递推关系:大问题拆成更小的同类问题(调用自己)。
向结束条件递进:每次递归都要让参数"更接近"基线,否则永远到不了终止条件。
第一个案例(求5的阶乘)
第二个案例(斐波那契数列)
# 1 1 第三个数开始是前两个数之和
# 1 1 2 3 5 8 13 21 34 ...
# 定义一个函数 求第n个斐波那契数列的数
# 使用递归
# 1.必须让递归有一个结束条件
# 2.找规律调用自己(重点!!!!!)
# 3.向结束条件逼近
总结
┌──────────────────────────────────────────────┐
│ 递归三要素: │
│ ① 基线条件:最小情况,直接返回 │
│ ② 递推关系:拆成更小的同类问题 │
│ ③ 向基线收敛:参数越来越接近基线 │
│ 风险:缺基线/不收敛 → 栈溢出 RecursionError │
└──────────────────────────────────────────────┘
作用域
全局变量
┌──────────────────────────────────────────────────┐
│ 全局变量:函数外部定义,整个文件都能用 │
│ 函数内想修改它,必须加 global 声明 │
└──────────────────────────────────────────────────┘
nonlocal 关键字
需求:内层函数默认改不了外层嵌套变量。如果确实要改(比如做计数器),怎么办?用nonlocal。
分析:nonlocal与global容易混,区别如下:
| 关键字 | 作用对象 | 使用场景 |
|---|---|---|
global | 文件顶层的全局变量 | 在函数里改全局变量 |
nonlocal | 外层(非全局)嵌套函数里的变量 | 在更内层函数里改外层函数的变量 |
nonlocal只能用于嵌套函数,且必须在修改前声明。
lanbda(匿名函数)
lambda是"匿名函数",适合写只有一行逻辑的小函数。
公式:lambda 参数:表达式
闭包和装饰器
闭包
需求:有个外层变量count,函数执行完本该被销毁;但我希望"函数结束后,内层函数还能记住并修改这个外层变量"——这就是闭包要解决的。
分析:闭包 = 带着"环境背包"的函数。它必须同时满足三个条件(缺一不可):
┌──────────────────────────────────────────────────────┐
│ 闭包三大条件 │
├──────────────────────────────────────────────────────┤
│ ① 函数嵌套:内层函数定义在外层函数内部 │
│ ② 变量引用:内层函数引用了外层函数的变量(非全局) │
│ ③ 返回内层函数:外层函数 return 内层函数本身 │
└──────────────────────────────────────────────────────┘
闭包的核心价值:把"状态"保存下来。外层函数结束后,内层函数依然"背着"当时的外层变量,每次调用都能累积。
预期效果:
命令 / 代码参数解读
return counter— 注意没有括号,返回的是函数对象本身,不是调用结果。所以c1 = create_counter()拿到的是一个"记住了count=0"的函数。c1()每次调用,counter里的nonlocal count修改的是那个被记住的外层count,于是不断累加:1 → 2 → 3。c2是另一次create_counter()创建的,有自己独立的count,所以c2()又从 1 开始。c1.__closure__是一个元组,装着闭包捕获的每个外层变量("cell");cell_contents就是它当前的值3。
小结:
闭包 = 内层函数 + 它记住的外层变量。常见用途:计数器、配置缓存、装饰器底层。
装饰器(传统&@语法糖)
需求:已有十几个函数,现在想给它们统一加上"执行前打印日志 / 计时"的功能,但不想改这些函数本身的代码(开闭原则:对扩展开放、对修改关闭)。
传统
语法糖
扩展知识点:
sort:对列表内的数据进行排序
sorted&reverse:排序&反转顺序