想象你是厨房里的主厨。今天客人点了一道番茄炒蛋,你翻开随身携带的食谱卡,三步五步就炒出一盘香气扑鼻的菜。明天客人又点同样的菜,你不需要重新发明做法——因为食谱就在那里,照着做就行。这就是函数的本质:把一段逻辑写成“食谱”,反复调用,永远不需要重写第二遍。
Python 的世界里,食谱就是 def 定义出来的函数。食材是参数(parameter),出锅的菜是返回值(return value)。你可以在食谱上写“如果客人没交代,就默认放两勺糖”——这叫默认参数。有些食材论顺序放,有些按名字指——这是位置参数与关键字参数的区别。遇到“想放多少食材就放多少”的创意菜?可变参数 *args 和 **kwargs 就是那只万能的塑料袋。
更妙的是,Python 里还有一张“便签纸食谱”——lambda 表达式,适合那些只需用一次、不必兴师动众定义完整函数的场景。比如排序时临时指定一个比较规则,lambda 就像口袋里随取随用的调味勺。学完这一篇,你将能从“只会照着教程写脚本”进阶到“能自己搭建整洁、可复用的代码厨房”,并为后续学习装饰器、函数式编程打下最扎实的地基。
对 AI 从业者而言,函数与 lambda 是构建整洁、可复用数据处理管线的基石。无论是写数据清洗脚本、构造特征工程,还是理解深度学习框架中无处不在的函数回调,这一课都是你从“能跑”走向“会设计”的分水岭。
可视化演示
展示fib(5)递归调用的展开过程,从根节点逐层展开至基准情况,再逐层返回计算结果。
本节配有交互动效(进入视口后自动加载,约 3KB)。
递归调用树 — fib(5) 根节点fib(5)开始递归展开 每个节点分裂为两个子调用fib(n-1)与fib(n-2) fib(n)=fib(n-1)+fib(n-2) 叶子节点fib(1)=1和fib(0)=0为基准情况
函数基本概念
在厨房里,最让主厨安心的不是某个拿手菜,而是那本随时翻开的食谱卡。今天客人点番茄炒蛋,你翻出卡片照做;明天客人又点,你不需要重新发明创造。食谱把“怎么做”固化下来,这就是最初级的代码复用——一次书写,无数次执行。
Python 中的 def 关键字就是写食谱卡的动作。def 后面跟函数名、圆括号里的参数列表,以及冒号后缩进的代码块。这段代码不会在定义时立即执行,而是像食谱卡一样被小心存放,直到你用 函数名() 的形式去“点菜”——这叫函数的调用(call)。
函数还能附带 docstring,即写在函数体第一行的字符串,相当于食谱卡背面的提示笔记,说明这道菜适合什么场合。Python 内置的 help() 和 IDE 的智能提示都会读取它。此外函数在 Python 中是一等对象(first-class object),意味着函数可以像普通变量一样被赋值、被当作参数传递、被放进列表——这为后面的闭包、装饰器打下了地基。
有了食谱卡,重复劳动戛然而止。同样,在代码世界里,凡是出现三次以上的逻辑,你都会下意识地想:要不要把它单独写成函数?这是从“脚本小子”走向“代码主厨”的第一道门槛。
def 定义函数 f,f 接收参数,经过函数体的加工,把结果送回调用方。就像食谱卡从“备菜”到“上菜”的完整过程。- def 与函数名 f
def是创建函数的声明,f是这张“食谱卡”的名字。函数名与变量名遵循同样的命名规范,如make_dish这样的蛇形命名(snake_case)。 - 参数列表 params写在圆括号里,是这道菜需要的食材清单。可以没有参数,也可以有多个参数。调用时必须按函数定义的约定提供食材。
- 冒号与函数体冒号表示函数体开始,缩进的代码块是具体做菜步骤。调用时,Python 会执行这段代码块。
- 调用与返回
f(params)代表一次执行,执行后通过return把结果送给调用方,就像把做好的菜端出厨房。
参数传递机制
客人点菜时,讲究的会说“两个番茄,三个鸡蛋”,这是按顺序报的;急性子的会喊“鸡蛋三个!番茄两个!”虽然顺序乱了,但菜名和数量都清晰,主厨也能听懂。前者好比位置参数(positional argument),后者好比关键字参数(keyword argument)。
位置参数按顺序依次匹配形参,少传或多传都会报错;关键字参数用 形参名=值 的方式传参,可以无视顺序,代码的可读性也更强。两种方式还可以混用,但必须把位置参数放在关键字参数前面,这也是 Python 的硬性语法。
默认参数(default argument)让形参在调用方缺省时自动获得预设值,就像食谱上写的“糖两勺,如果客人没交代就默认”。而 *args 和 kwargs 是两颗彩蛋:*args 把多出来的位置参数打包成一个元组,kwargs 把多出来的关键字参数打包成一个字典——相当于万能食材袋,无论食客怎么加料都能接住。
参数的灵活设计,决定了你写的函数像不像一间好餐厅:既给了常客固定的招牌菜(位置参数),也照顾了挑食客人的个性化需求(关键字与默认值),还接纳了突发的大规模宴席(*args 与 kwargs)。
- 普通位置参数 a必须由调用方提供,像主餐食材,缺了菜就做不成。
- 默认参数 b=1可省可不省。如果不传就使用默认值,就像“没交代就默认放一勺糖”。
- 可变位置参数 \*args收集多余的“零散食材”,全部装进一个元组。比如调用
f(1, 2, 3, 4)时,args=(2, 3, 4)。 - 可变关键字参数 \*\*kwargs收集多余的关键字信息,装进一个字典。比如调用
f(1, salt='少许')时,kwargs={'salt': '少许'}。
返回值与解包
一道菜做完,主厨要把成品端到客人面前。函数也一样,return 语句负责把计算结果“端出”函数,交到调用者手里。如果函数没有 return,Python 会在函数结束时默认返回 None,就像上了一盘空气。
return 还意味着流程终止,就像端菜之后不会再回锅翻炒——return 之后的代码不会执行。这一点常被用来做早退(early return)判断:先处理异常和边界条件,让主逻辑留在函数最后。
Python 允许一次 return 返回多个值,比如 return x, y,实际上返回的是一个元组 (x, y)。配合序列解包(unpacking),调用方可以一句 a, b = f() 把结果拆开。这种“多菜同盘”的技法在返回坐标、状态码+消息等场景中非常常用。
恰当的返回值设计,是好函数与坏函数的分水岭。就像好餐厅的菜品稳定且分量清晰,你的函数应该每次都给出可预期的结果,并且调用方一眼就知道拿到的盘子里是什么。
return x, y 的逗号会自动把多个值打包成元组,因此调用方收到的其实是一个整体。- return 关键字触发函数结束并返回结果;没有该语句时,函数返回内置的
None对象。 - 多返回值打包成元组
return x, y里的逗号让 Python 自动构造(x, y)元组,等价于return (x, y)。 - 解包赋值调用方用
a, b = f()自动拆开元组,像拆开一份双拼饭盒,一格菜一格饭各就各位。
作用域LEGB
一间大厨房分几个区域:炉灶旁触手可及的调料架、水槽边的备用柜、储藏室的大米袋,还有整栋楼公用的基础调料区。厨师在不同的位置,能直接够到的东西不一样——这就是“作用域”的直观感受。
Python 对名称的查找遵循 LEGB 规则:Local(局部)→ Enclosing(嵌套外层)→ Global(全局)→ Builtin(内置)。也就是说,函数内部先找局部变量,找不到再去外层嵌套的函数里找,再找不到去模块全局找,最后去 Python 内置的名称(如 len、print)里找。
如果要在函数内部修改一个全局变量,需要声明 global;要修改嵌套外层函数的变量,则需要 nonlocal。否则,Python 会把赋值语句左侧的变量一律视为局部变量,初学很容易踩 UnboundLocalError 的坑。这就像厨房里:你想挪动储藏室里的大米,得先跟管理员打声招呼(global),否则你只是在工作台旁新放了一袋米。
理解 LEGB,就像主厨记住厨房地图:既不会把手伸进别人的锅里(局部变量隔离),也知道上哪儿找公共资源(内置函数)、何时该申请权限(global/nonlocal)。干净的作用域管理,让代码天然低耦合。
- Local(局部)当前函数内部声明的变量,相当于炉灶边的调料架,最优先被查找。
- Enclosing(嵌套)如果当前函数内嵌于另一个函数,外层函数的局部变量也在搜索范围中,如同隔壁料理台的备用碗柜。
- Global(全局)当前模块(
.py文件)顶层的变量,像储藏室的大米和面粉桶。 - Builtin(内置)Python 解释器预置的名称,如
print、len,像整栋楼公用的基础水电设施,谁都能用。
闭包机制
一位主厨离开这家餐厅之后,随身带了一个便当盒,盒里装着他最爱的几味秘制调料。此后无论他去哪家餐厅做菜,打开便当盒就能还原那独特的味道——这个便当盒,就是闭包(closure)。
在 Python 中,当一个内层函数引用了外层函数的局部变量,并且外层函数把这个内层函数作为返回值返回时,内层函数连同它所引用的变量环境,就构成了闭包。外层函数的局部变量此时以“自由变量”的身份被内层函数记住,即使外层函数已经执行完毕,这些变量的值也不会消失。
如果要修改自由变量,需要在内层函数中使用 nonlocal 声明。闭包在需要携带状态又不想引入类的场景中特别有用,例如计数工厂、装饰器、回调函数。可以说闭包是把“数据 + 行为”打包的最小单位。
闭包是厨师最重要的私藏技艺,也是理解装饰器的必经台阶。当你写出一个“返回函数的函数”时,你已经站在 Python 高级特性的门口了。
- 外层函数 outer 与参数 x
x既可以是 outer 的形参,也可以是局部变量,它将被内层函数捕获。 - 内层函数 inner在 outer 内部定义,并作为返回值返回,形同被带回的便当盒。
- 捕获自由变量 x
inner内部使用了x,这个x并未在inner内定义,而是来自 outer,因此被称为自由变量。 - 生存期延长outer 返回后,
x原本的栈帧本应销毁,但闭包让inner继续持有x,值不丢失。
lambda表达式
有时主厨并不会为一道简单小菜专门写一整页食谱,他会随手撕一张便签,写上“蒜末 + 生抽 + 香油 + 醋 = 凉拌汁”。这只在当下有用,但它足够快速、足够直接——这就是 lambda 表达式的气质。
lambda 的完整语法只有一行:lambda 参数: 表达式。它不需要 def、不需要函数名、不需要 return,表达式的值自动成为返回值。例如 lambda x: x * 2 等价于 def f(x): return x * 2。
但 lambda 有着严格的自我限制:体内只能写一个表达式,不能包含赋值语句、多行逻辑或文档字符串。这让 lambda 天然适合那些简单、临时、定义完马上用掉的场合。一旦逻辑变复杂,就说明该用 def 写一道正式食谱了。
用 lambda 就像用便签:写的时候痛快,但如果你发现这张便签开始承担复杂的流程,就该把它誊写为正式食谱。判断标准永远是“可读性优先”。
- lambda 关键字声明一个匿名函数,它是“便签纸”的标题,不是正式食谱封面。
- 参数 x与 def 的参数列表一样,可以是位置参数、默认参数甚至
*args,多个参数用逗号分隔。 - 冒号与表达式 expr冒号后面只能有一个求值表达式,不能有赋值等语句;表达式的结果就是返回值。
- 与 def 的等价关系理论上一对一映射,但 def 能承载更多行逻辑、文档和语句。
lambda高阶应用
便签食谱最常见的用法,是把它传递给其他工具。Python 的 sorted 函数接受一个名为 key 的形参,用来指定“按什么排序”——你给出一张便签,sorted 就会按便签上的规则把食材排好队。
例如 sorted(dishes, key=lambda d: d["time"]),会按每道菜的耗时从小到大排列。key 参数接收一个“把一个元素变成排序依据”的函数,lambda 在这里几乎是最自然的选择。类似的还有 map(lambda x: x * 2, data) 对每个元素做变换,filter(lambda x: x > 0, data) 筛选元素。
但这里有一个著名陷阱:在循环中创建 lambda 并立刻引用循环变量,会因为闭包捕获了变量本身(而非当时的取值)而导致所有 lambda 最终看到同一个值。比如 [lambda: i for i in range(3)] 这三个函数都返回 2。解决方法是给 lambda 绑定默认参数 lambda i=i: i,把当前值“快照”进便签。
lambda 是把双刃剑:用对了是高效调味,滥用则让代码变成无人能懂的乱码。高阶 Python 的成熟度不在于“用了多酷的语法”,而在于知道何时该用、何时该克制。
- sorted 与 key
sorted按key函数返回的值对元素排序,key只读不改。 - lambda 提取排序依据对每个元素
v提取其索引 1 处的字段(如耗时、得分),作为排序依据。 - 捕获陷阱与修复循环内直接用循环变量时,lambda 捕获的是引用而非当时的取值;绑定默认参数
i=i可解除。
AI 应用场景
数据清洗流水线中的字段映射
阶段:数据预处理与特征工程
解决的问题:原始日志里的字段经常带着单位、脏字符,比如价格字段是 "¥12.5"、温度字段是 "37°C"。传统做法是写大量重复的 for 循环逐个清理,代码冗长且易错。用函数封装“清洗规则”,再配合 lambda 把规则套用到每个字段,一次封装处处复用。
具体案例:某风控团队的 AI 工程师从交易日志中提取特征,写了 transform_column(data, func) 函数,调用时用 lambda x: float(x.replace("¥", "")) 批量转换价格字段,用 lambda x: x.lower().strip() 清洗地址字符串,原本 200 行的清洗脚本缩减到 30 行。
模型评估中的候选排序
阶段:模型训练与调参评估
解决的问题:训练完多个候选模型,需要按 AUC、F1、耗时等多个指标综合排序。传统方式要写多重嵌套排序逻辑,可读性差。使用 sorted(models, key=lambda m: m["f1"], reverse=True) 一行完成按指标排序,还能快速切换排序字段。
具体案例:在 NLP 分类任务中,一位算法工程师对 5 个候选模型做了交叉验证,用 key=lambda m: (m["f1"], -m["latency_ms"]) 实现“先按 F1 降序、再按时延升序”的复合排序,快速锁定了最终上线的模型。
指标计数器的闭包缓存
阶段:推理服务与监控告警
解决的问题:线上模型推理往往需要统计某个特征被触发的次数,传统做法是引入类或全局变量,代码冗余。闭包可以把“计数状态”和“计数行为”封装在一起,就像给监控系统加了一台自带计数功能的仪表。
具体案例:一个推荐系统团队需要统计每小时各推荐策略的曝光次数,用 make_counter() 闭包工厂生成独立计数器,每个策略各持有一只“便当盒”,既灵活又不污染全局命名空间,配合定时器上报给监控平台。
Python 演示
"""食谱厨房:函数与lambda实战演示""" def make_dish(name, *ingredients, cooking_time=10, **options): """把食材列表变成一道菜的字典。""" if not ingredients: raise ValueError(f"{name} 缺少食材,无法下锅") dish = {"name": name, "ingredients": list(ingredients), "time": cooking_time} dish.update(options) return dish def make_sauce(flavor): """闭包:生成一个固定风味的调味函数。""" def sauce(): return f"{flavor}味酱汁" return sauce def main(): # 1. 位置参数 + 默认参数 + 关键字参数 dish1 = make_dish("番茄炒蛋", "番茄", "鸡蛋", cooking_time=8, chef="小王") print(dish1) # 2. 可变参数把零散食材收进元组 dish2 = make_dish("大杂烩", "土豆", "牛肉", "胡萝卜", "洋葱", cooking_time=25) print(dish2) # 3. 闭包:走到哪里都带着秘制酱汁 secret = make_sauce("花椒") print(secret()) # 4. lambda 作为排序规则 menu = [ {"name": "番茄炒蛋", "time": 8}, {"name": "红烧肉", "time": 40}, {"name": "拍黄瓜", "time": 3}, ] quickest = sorted(menu, key=lambda d: d["time"]) print("最快上菜:", [d["name"] for d in quickest]) # 5. lambda 捕获陷阱与修复 funcs = [lambda i=i: i for i in range(3)] print("快照修复:", [f() for f in funcs]) if __name__ == "__main__": main()
def make_dish(name, *ingredients, cooking_time=10, **options):一行同时声明位置参数、可变参数、默认参数和关键字参数,覆盖全部传参方式。if not ingredients:判断食材元组是否为空,缺少食材直接抛出异常,体现“早退”防御式编程。dish.update(options)把chef等额外关键字参数合并进结果字典,**options大显身手。def make_sauce(flavor):外层函数,flavor将成为闭包捕获的自由变量。return sauce返回内层函数本身,让“便当盒”被带到函数外部。secret = make_sauce("花椒")拿到一个自带花椒风味的函数,闭包状态随身携带。quickest = sorted(menu, key=lambda d: d["time"])用 lambda 提取排序关键字,一行完成按耗时排序。funcs = [lambda i=i: i for i in range(3)]绑定默认参数把循环变量“快照”,演示修复闭包捕获陷阱的标准写法。
常见误区
误区很多初学者写完 `def` 缩进块,就觉得代码“跑了”,结果程序没有一点输出,还以为自己写错了。
正解def 只是登记了一张食谱卡,把代码对象绑定到函数名上。只有加括号调用 函数名(),函数体才会真正执行。就像食谱卡放在抽屉里并不会自动做菜,只有你拿出来照做才行。先区分“定义”与“调用”,是学会函数的第一课。
为何常见:平时写脚本都是从上到下立即执行,遇到“定义”和“调用”两个概念的分离,直觉上容易混淆,误以为缩进块本身就是执行信号。
误区总以为 `*args` 里的 `args` 是关键字,必须叫这个名字才能生效。
正解* 和 ` 才是语法核心,后面的名字只是变量名。*items、options` 完全一样有效,但要保证同一个函数里形参名不冲突。就像说“任意数量的食材”,袋子本身叫什么不重要,重要的是那个星号表明“把这些东西打包”。
为何常见:教程里到处用 *args/kwargs,看得多了自然就以为名字是硬性规定,而忽略了真正的语法标记是星号。
误区在函数里写 `count += 1`,就以为修改了外面的全局 `count`。
正解对变量赋值时,Python 默认把该变量看作局部变量;要修改全局变量本身必须声明 global。否则会因为“先赋值后使用”的逻辑报 UnboundLocalError。这就像在厨房工作台旁新放了一袋米,不等于移动了储藏室里那袋米——赋值动作默认是在“当前领地”里新建名字。
为何常见:“离得最近的那个变量”这个直觉在赋值语义下失效,很多人没意识到赋值本身就是“创建新名字”的动作,而非纯粹地修改旧东西。
误区觉得 lambda 写起来更短更酷,于是所有函数都用 lambda,结果代码变得难以阅读和维护。
正解lambda 只能包含一个表达式,不支持赋值语句、多行逻辑和文档字符串。一旦逻辑超过一行,可读性急剧下降,调试也困难。正式食谱该用 def,便签纸不该用来写满汉全席。工程上更看重“团队能看懂”,而不是“一行写完”。
为何常见:演示代码里 lambda 极其简洁、很有冲击力,容易让人高估它的能力边界,忽略了它“只适合短小临时逻辑”的本质。
误区以为 `sorted` 会像 `list.sort()` 一样原地修改原列表,于是后续数据被弄乱,引发一系列隐蔽 bug。
正解sorted 返回一个全新的排序后列表,原列表保持不动;list.sort() 才原地修改。lambda 只是告诉 sorted 按什么标准比较,不会改写数据。这就像你照食谱重新抄了一份清单,原来的冰箱并没有变。需要原地排序时再用 list.sort()。
为何常见:sorted 和 sort 名字极像,很多入门资料把二者混用,导致功能误记。二者一个“返回新菜”,一个“重炒旧菜”。
面试题
Q1. 关于 Python 函数的 def 定义,下列说法最准确的是?
- A.def 语句执行时会立刻运行函数体
- B.def 仅定义函数对象,调用时才执行← 正确答案
- C.函数可以凭空使用,不依赖任何定义
- D.def 必须放在文件最开头才能使用
答案:B。def 只是把函数体打包成函数对象并绑定到名称上,并不会立即执行函数体。只有加括号调用 f() 时才会执行——就像把食谱卡放进抽屉并不等于菜做好了。A 混淆了定义和调用;C 完全违背函数必须先定义后使用的原则;D 中 def 可以出现在任何位置,只是调用时要求已经定义。
Q2. 关于默认参数 b=1,下列哪种说法正确?
- A.每次调用函数时都要重新计算默认值
- B.默认值在函数定义时便已经固定下来← 正确答案
- C.默认值只在第一次调用时计算出结果
- D.默认值在程序运行结束时才进行求值
答案:B。Python 在函数定义时就会对默认参数求值并固定下来,后续调用都复用同一个对象。这就像食谱卡上的“默认放两勺糖”在写卡时就定了,不会每次做菜重新决定。因此默认参数不能使用可变对象(如列表),否则会跨调用共享,产生“上次客人留下的食材污染下次菜”的诡异 bug。
Q3. 执行 def f(a, b, *args): pass,再调用 f(1, 2, 3, 4),args 的值是?
- A.一个包含 3 和 4 的元组对象← 正确答案
- B.一个包含 3 和 4 的列表对象
- C.一个包含 3 和 4 的集合对象
- D.两个彼此独立的普通参数对象
答案:A。*args 会把多出来的位置参数打包成元组(tuple)。a=1, b=2 被前两个形参接收,剩下的 3, 4 进入 args,即 args=(3, 4)。它不是列表、不是集合,也不是两个独立参数,而是“一个装了两颗食材的保鲜袋”。如果换成 kwargs,则是打包成字典。
Q4. 在函数内部查找一个变量时,Python 按什么顺序搜索各作用域?
- A.局部到外层再到全局再到内置← 正确答案
- B.全局再到局部再到内置再到外层
- C.内置到外层再到全局再到局部
- D.外层到局部再到内置再到全局
答案:A。LEGB 规则即 Local → Enclosing → Global → Builtin,由内向外逐层查找,先找到谁就用谁。就像厨师先看灶台边调料架(局部),再看隔壁备用柜(嵌套),然后去储藏室(全局),最后用整栋楼的基础水电(内置)。其余三个选项的次序都不符合 Python 规定。
Q5. 关于闭包,下列说法正确的是?
- A.闭包可以捕获外层函数的局部变量← 正确答案
- B.闭包可直接修改外层变量而无需声明
- C.闭包由内层函数引用外层变量构成← 正确答案
- D.闭包变量在外层函数结束后全部遗失
答案:AC。闭包的核心是内层函数引用了外层函数的局部变量(自由变量),并且外层函数将这些变量“打包”在返回的函数里,因此 A、C 正确。B 错误:要修改自由变量必须 nonlocal 声明,否则会在内层新建局部变量。D 错误:闭包的标志性能力恰恰是外层函数结束后,自由变量依然存活。这就像带着便当盒离开厨房后,秘制调料还在手边。
Q6. lambda 表达式体内不能包含下列哪一项?
- A.简单的四则算术运算表达式
- B.三元条件表达式这种单行写法
- C.赋值语句这类有副作用的语句← 正确答案
- D.对其它已定义函数的调用语句
答案:C。lambda 体内的语法限制是“只能有一个表达式”,因此赋值语句(如 x = 1)、多行逻辑或 return 都不能出现。代码 lambda x: x = 1 会直接语法报错。算术、三元表达式、函数调用本质上都是“求出某个值”的表达式,所以允许。这就像便签纸只能写一行配方,写不了多步骤操作手册。
Q7. 执行 sorted([('a', 3), ('b', 1)], key=lambda x: x[1]) 的结果是?
- A.列表保持原顺序
[('a', 3), ('b', 1)] - B.变成
[('b', 1), ('a', 3)]按第二项升序← 正确答案 - C.原列表被原地修改并按第二项排序
- D.抛出 TypeError 参数类型不匹配错误
答案:B。sorted 的 key 参数指定排序依据,lambda x: x[1] 让每个元组按索引 1 处的值比较,因此 1 排前面,3 排后面。同时 sorted 返回新列表,原列表不变,所以 C 错误。A 是原顺序,D 是莫须有的错误——lambda 在这里完全合法。
Q8. 关于函数返回值,下列说法正确的有?
- A.没有 return 语句时函数返回 None← 正确答案
- B.return 多个值实际返回一个元组对象← 正确答案
- C.return 之后的代码仍然会被继续执行
- D.返回值数量完全由函数的调用方决定
答案:AB。没有 return 时函数自动返回 None,A 正确。return x, y 的逗号会构造元组,B 正确。return 一旦执行,函数立即结束,后面的代码不会运行,C 错误。返回值数量由函数定义与 return 语句决定,不由调用方决定,D 错误。这就像菜端出厨房后不会回锅续炒,上几道菜由后厨说了算。
Q9. 关于 kwargs 可变参数,下列哪项描述最准确?
- A.接收位置参数并打包成一个元组对象
- B.接收关键字参数并打包成一个字典对象← 正确答案
- C.接收列表参数并把列表展开成单个元素
- D.只能用于 lambda 表达式的简单参数场景
答案:B。kwargs 专门收集调用时多出来的关键字参数,打包成字典(dict)。比如 f(a=1, salt='少许') 中 kwargs={'salt': '少许'}。A 描述的是 *args 的行为;C 中“展开列表”是调用侧的 *list 语法,与 kwargs 无关;D 中 lambda 也支持 kwargs,但并非“只能用于”。
Q10. 关于函数与 lambda 的关系和特性,下列哪些说法正确?
- A.lambda 适用于简单单表达式的临时场景← 正确答案
- B.lambda 能够拥有自己的独立文档字符串
- C.函数是一等对象,可以赋值给变量使用← 正确答案
- D.函数体内部还可以继续定义新的函数← 正确答案
答案:ACD。lambda 因为只能写单表达式,所以适合临时小逻辑,A 正确。函数在 Python 中是一等对象,可作为参数传递、赋值给变量、装进列表,C 正确。函数体内可以嵌套定义新函数,这种结构是闭包的基础,D 正确。B 错误:lambda 没有独立的文档字符串语法,它只是一个匿名表达式,无法像 def 那样用 docstring 说明用途——便签纸写不下使用说明书。