Python函数-匿名函数、生成器、内置函数和推导式
一、匿名函数
1、定义
lambda 参数列表: 单表达式会即时返回一个函数对象,没有名字,通常作为一次性、短小逻辑的“即用即弃”函数。
2、基本语法
| 位置 | 写法举例 | 说明 |
|---|---|---|
| 无参 | lambda: 42 | 直接返回 42 |
| 单参 | lambda x: x*2 | 输入乘以 2 |
| 多参 | lambda x, y, z=10: x+y+z | 支持默认参数 |
| 可变参 | lambda *a, **kw: len(a)+len(kw) | 支持 *args、**kwargs |
注意:
- 只能有一个表达式,不能出现赋值、for、if-else 语句(三元表达式可以)。
- 返回值就是表达式的值,无需
return。
3、作用域规则(与 def 完全一致)
- 遵循 LEGB;
- 同样形成闭包:
fs = [lambda x: x + i for i in range(3)]print([f(0) for f in fs]) # [2, 2, 2]
# 这里 i=i 把循环变量冻结到默认参数,避免“延迟绑定陷阱”。fs = [lambda x, i=i: x + i for i in range(3)]print([f(0) for f in fs]) # [0, 1, 2]lambda 闭包的语法就是:
lambda 形参1, 形参2=默认值 : 表达式- 只要表达式里出现外层局部变量,就形成闭包;
- 在循环里务必用“默认参数”把值钉死,否则所有匿名函数共享同一个变量。
4、与 def 的对比
| 维度 | lambda | def |
|---|---|---|
| 名称 | 匿名 | 必须显式命名 |
| 主体 | 单表达式 | 多语句块 |
| 结果 | 隐式 return | 可显式 return |
| 可读性 | 极短逻辑 | 长逻辑更清晰 |
| 调试 | 栈跟踪显示 <lambda> | 显示函数名 |
| 注解 | 无法直接写注解 | 支持完整的函数签名 |
5、常见误区
5.1、“lambda 比 def 快”
- 执行速度几乎一样;lambda 只是省去了栈帧名字绑定。
5.2、循环变量陷阱
fs = [lambda: i for i in range(3)]print([f() for f in fs]) # [2, 2, 2]
# 解决:用默认参数或改用 def。5.3、试图写多行
lambda x: y = x*2; return y # SyntaxError
# 长逻辑请用 def。6、进阶技巧
- 立即调用:
(lambda x: x**2)(5) - 与高阶函数搭配:
map、filter、sorted的key参数 - 柯里化:
from functools import partialmul = lambda x, y: x * ydouble = partial(mul, 2)7、典型场景
7.1、sorted 的 key 函数
sorted(students, key=lambda s: s.score, reverse=True)7.2、GUI 回调 / CLI 框架
button.clicked.connect(lambda: print('clicked'))7.3、小脚本中的过滤
evens = list(filter(lambda n: n % 2 == 0, range(10)))二、三元运算符
1、语法结构
value_if_true if condition else value_if_falsecondition:一个布尔表达式,其结果为 True 或 False。value_if_true:如果condition为 True,则返回这个值。value_if_false:如果condition为 False,则返回这个值。
2、基本用法
x = 10y = 20
# 使用三元运算符result = x if x > y else yprint(result) # 输出 20- 在这个例子中,
x > y是条件。因为x不大于y,所以条件为False,返回y。
3、更复杂的表达式
a = 5b = 10
# 使用三元运算符嵌套result = "Both are equal" if a == b else "a is greater" if a > b else "b is greater"print(result) # 输出 "b is greater"4、与传统 if-else 的对比
4.1、传统 if-else
x = 10y = 20
if x > y: result = xelse: result = y
print(result) # 输出 204.2、三元运算符
x = 10y = 20
result = x if x > y else yprint(result) # 输出 20- 三元运算符在表达简单逻辑时更加简洁,但当逻辑复杂时,建议使用传统的
if-else结构,以提高代码的可读性。
5、注意事项
- 可读性:虽然三元运算符很简洁,但嵌套使用时可能会降低代码的可读性。尽量避免过度嵌套。
- 短路行为:三元运算符具有短路行为。如果条件为 True,则不会计算
value_if_false;如果条件为 False,则不会计算value_if_true。 - 类型一致性:虽然 Python 允许
value_if_true和value_if_false是不同类型,但为了代码的可读性和一致性,建议它们是相同类型。
6、实际应用场景
6.1、设置默认值
user_input = input("Enter a number: ")number = int(user_input) if user_input.isdigit() else 0print(number) # 如果输入不是数字,默认为 06.2、简化逻辑
# 传统写法if condition: result = value1else: result = value2
# 三元写法result = value1 if condition else value26.3、条件赋值
# 根据条件选择字符串message = "Success" if status_code == 200 else "Error"三、生成器(Generator)
1、定义
- 生成器是一种可迭代对象,它逐个生成值,而不是一次性返回所有值。生成器函数通过
yield关键字返回一个生成器对象。
2、生成器的核心特点
- 惰性求值:值在需要时才生成
- 内存高效:不需要存储整个序列在内存中
- 可恢复状态:每次调用从上次暂停处继续执行
- 无限序列:可以表示无限长的数据流
3、创建生成器的两种方式
3.1、生成器函数
- 使用
def定义函数,并在函数体内使用yield语句
def count_up_to(max): count = 1 while count <= max: yield count count += 1
gen = count_up_to(100)print(gen)
输出:<generator object count_up_to at 0x7f24ef254890>3.2、生成器表达式
- 类似列表推导式,但使用圆括号
()
squares = (x*x for x in range(10))4、生成器的工作流程(图示)
调用生成器函数 │ ▼创建生成器对象 ────────────────┐ │ │ ▼ │首次调用 next() │ │ │ ▼ │执行到第一个 yield 暂停 ◄──────┤ │ 返回 yield 的值 │ ▼ │后续调用 next() │ │ │ ▼ │从上个 yield 后继续执行 ◄──────┘ │ ▼遇到 return 或函数结束 │ ▼抛出 StopIteration 异常5、生成器函数 vs 普通函数
| 特性 | 普通函数 | 生成器函数 |
|---|---|---|
| 返回值 | 使用 return | 使用 yield |
| 执行方式 | 一次执行完成 | 可暂停和恢复 |
| 返回值 | 单次返回值 | 可多次返回值 |
| 状态 | 每次调用独立 | 保持上次执行状态 |
| 内存使用 | 可能占用大量内存 | 内存高效 |
6、生成器的内存模型(图示)
普通列表的内存使用:┌──────────────┐│ 列表对象 ││ ││ [0,1,4,9,16] │ 存储所有元素└──────────────┘ ▲ │ └─ 占用连续内存块
生成器的内存使用:┌──────────────┐ 按需生成值│ 生成器对象 │ ┌───┐ ┌───┐ ┌───┐│ 状态: count=3 │───▶│ 0 │▶│ 1 │▶│ 4 │ ...│ 代码位置 │ └───┘ └───┘ └───┘└──────────────┘ 每次只保留 当前状态和代码位置7、生成器的生命周期(代码演示)
def number_generator(n): print("生成器启动") for i in range(n): print(f"即将生成 {i}") yield i print(f"恢复执行") print("生成器结束")
# 创建生成器gen = number_generator(3)
# 逐步执行print("第一次调用:")print(next(gen)) # 输出: 生成器启动, 即将生成 0, 0
print("\n第二次调用:")print(next(gen)) # 输出: 恢复执行, 即将生成 1, 1
print("\n第三次调用:")print(next(gen)) # 输出: 恢复执行, 即将生成 2, 2
print("\n第四次调用:")try: print(next(gen)) # 输出: 恢复执行, 生成器结束except StopIteration: print("生成器已耗尽")8、生成器next()、for 循环取值比较
8.1、一段最小生成器
def func(): yield 1 yield 2 yield 38.2、三种拿值方式对比
| 方式 | 语法 | 得到 StopIteration 时的行为 | 常见场景 |
|---|---|---|---|
next() | next(gen) | 抛出 StopIteration | 手动驱动,一次拿一个 |
for 循环 | for x in gen: | 自动捕获并结束循环 | 遍历整个序列 |
| 列表/集合推导式 | [x for x in gen] | 自动捕获并结束推导 | 一次性收集 |
8.3、运行流程逐帧图
生成器代码 外部代码 返回值─────────────────────────yield 1 ←─ next() ── 1yield 2 ←─ next() ── 2yield 3 ←─ next() ── 3函数结束 → StopIteration 异常- for 循环 把“异常→结束”这一步隐藏掉了,所以用户无感。
- 每次
next()或for循环的迭代都会让生成器从上次yield处继续往下跑。
8.4、代码示例:三种写法效果相同
gen = func()
# 1) 手动 next()print(next(gen)) # 1print(next(gen)) # 2print(next(gen)) # 3try: next(gen) # StopIterationexcept StopIteration: print("结束")
# 2) for 循环gen = func()for x in gen: print(x) # 1 2 3
# 3) 列表推导式gen = func()lst = [x for x in gen]print(lst) # [1, 2, 3]8.5、一句话总结
next(gen)是“手摇发动机”,每转一圈拿一个值;for循环是“自动发动机”,直到油尽(StopIteration)为止;- 两者背后都是同一条
yield管道,只是谁负责关掉水龙头。
9、注意事项
- 生成器是一次性对象,遍历后不能重用
- 使用
yield后函数返回生成器对象,不立即执行 - 需要手动调用
next()或使用循环启动生成器 - 生成器不存储所有值,不能随机访问
四、内置函数
abs() | delattr() | hash() | memoryview() | set() |
all() | dict() | help() | min() | setattr() |
any() | dir() | hex() | next() | slice() |
ascii() | divmod() | id() | object() | sorted() |
bin() | enumerate() | input() | oct() | staticmethod() |
bool() | eval() | int() | open() | str() |
breakpoint() | exec() | isinstance() | ord() | sum() |
bytearray() | filter() | issubclass() | pow() | super() |
bytes() | float() | iter() | print() | tuple() |
callable() | format() | len() | property() | type() |
chr() | frozenset() | list() | range() | vars() |
classmethod() | getattr() | locals() | repr() | zip() |
compile() | globals() | map() | reversed() | __import__() |
complex() | hasattr() | max() | round() |
abs(x):返回数字x的绝对值。all(iterable):如果可迭代对象iterable中的所有元素都为真(truthy),则返回True。any(iterable):如果可迭代对象iterable中有任何元素为真(truthy),则返回True。ascii(obj):返回对象obj的可打印字符表示形式。bin(x):将整数x转换为二进制字符串。bool(x):将x转换为布尔值,非零数值、非空序列等为True,否则为False。bytearray(iterable):返回一个可变的字节数组,由iterable中的元素构成。bytes(iterable):返回一个不可变的字节序列,由iterable中的元素构成。callable(obj):如果obj是可调用对象(如函数、方法等),则返回True。chr(i):返回整数i对应的 Unicode 字符。classmethod(func):将函数func转换为类方法。compile(source, filename, mode):将源代码source编译成代码对象。complex(real, imag):创建一个复数,其实部为real,虚部为imag。delattr(obj, name):删除对象obj的属性name。dict(**kwargs):创建并返回一个字典。dir():不带参数时,返回当前局部符号表的列表。带参数时,返回对象的属性列表。divmod(a, b):返回a除以b的商和余数。enumerate(iterable, start=0):返回一个枚举对象,它产生(index, value)对。eval(expression, globals=None, locals=None):计算字符串expression中的表达式,并返回结果。exec(object, globals=None, locals=None):执行动态生成的 Python 代码。filter(function, iterable):构造一个迭代器,返回使函数function返回值为真的那些元素。float(x):将x转换为浮点数。format(value, format_spec):将value格式化为字符串。frozenset(iterable):返回一个不可变的集合,由iterable中的元素构成。getattr(object, name[, default]):返回对象object的属性name,如果没有该属性则返回default。globals():返回当前全局符号表的字典。hasattr(object, name):如果对象object有属性name,则返回True。hash(object):返回对象object的哈希值。help(object):显示对象object的帮助信息。hex(x):将整数x转换为十六进制字符串。id(object):返回对象object的唯一标识符。input(prompt=''):显示提示信息prompt并返回用户输入的字符串。int(x[, base]):将x转换为整数,可选参数base指定进制。isinstance(object, classinfo):如果object是classinfo的实例或其子类的实例,则返回True。issubclass(C, B):如果类C是类B的子类,则返回True。iter(iterable, sentinel):返回迭代器对象,可选参数sentinel指定迭代结束条件。len(object):返回对象object的长度。list(iterable):返回列表,由iterable中的元素构成。locals():返回当前局部符号表的字典。map(function, iterable, ...):将函数function应用于iterable中的每个元素,并返回结果的迭代器。max(iterable, *args, key=None, default=None):返回iterable中的最大值。memoryview(object):返回对象的内存视图。min(iterable, *args, key=None, default=None):返回iterable中的最小值。next(iterator[, default]):返回迭代器的下一个元素,如果没有更多元素则返回default。object():返回一个新的、未命名的对象。oct(x):将整数x转换为八进制字符串。open(file, mode='r', ...):打开文件并返回文件对象。ord(c):返回字符c的 Unicode 码点。pow(base, exp[, mod]):返回base的exp次幂,可选参数mod指定取模。print(*args, sep=' ', end='\n', file=sys.stdout, flush=False):打印args中的值,用sep分隔,以end结尾。property(fget=None, fset=None, fdel=None, doc=None):返回一个属性对象。range(start, stop[, step]):返回一个整数序列。repr(object):返回对象的“官方”字符串表示形式。reversed(seq):返回一个反向迭代器。round(number[, ndigits]):返回浮点数number四舍五入到小数点后ndigits位的值。set(iterable):返回一个集合,由iterable中的元素构成。setattr(object, name, value):设置对象object的属性name为value。slice(stop):返回一个切片对象。sorted(iterable, *, key=None, reverse=False):返回iterable的排序列表。staticmethod(function):将函数function转换为静态方法。str(object='):将object转换为字符串。sum(iterable, start=0):返回iterable中元素的总和。super():返回一个代理对象,用于调用父类的方法。tuple(iterable):返回一个元组,由iterable中的元素构成。type(name, bases, dict):返回一个新的类型对象。vars(object):返回对象的__dict__属性。zip(iter1, ..., iterN):将多个迭代器中对应的元素打包成一个个元组,然后返回由这些元组组成的迭代器。__import__(name, globals=None, locals=None, fromlist=(), level=0):动态导入模块。
五、推导式
- Python 推导式(Comprehensions)是一种构建列表(list)、集合(set)、字典(dict)和生成器表达式(generator expressions)的简洁方式。推导式不仅代码更简洁,而且通常比等价的普通代码执行得更快,因为它们是专为迭代优化的。
1、列表推导式(List Comprehensions)
- 列表推导式提供了一种从其他列表构建新列表的简便方式。
1.1、基本语法:
[expression for item in iterable if condition]1.2、示例:
# 普通方式squares = []for i in range(10): squares.append(i * i)
# 列表推导式squares = [i * i for i in range(10)]print(squares) # 输出: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]2、集合推导式(Set Comprehensions)
- 集合推导式与列表推导式类似,但它生成的是集合。
2.1、基本语法:
{expression for item in iterable if condition}2.2、示例:
# 普通方式unique_numbers = set()for i in range(20): unique_numbers.add(i % 5)
# 集合推导式unique_numbers = {i % 5 for i in range(20)}print(unique_numbers) # 输出: {0, 1, 2, 3, 4}3、字典推导式(Dict Comprehensions)
- 字典推导式用于从键值对构建字典。
3.1、基本语法:
{key_expression: value_expression for item in iterable if condition}3.2、示例:
# 普通方式squared_dict = {}for i in range(10): squared_dict[i] = i * i
# 字典推导式squared_dict = {i: i * i for i in range(10)}print(squared_dict) # 输出: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}4、生成器表达式(Generator Expressions)
- 生成器表达式与列表推导式类似,但它生成的是一个生成器对象,该对象在迭代时才生成值,这使得它们在处理大数据集时更加内存高效。
4.1、基本语法:
(expression for item in iterable if condition)4.2、示例:
# 列表推导式numbers = [i for i in range(1000000)]
# 生成器表达式numbers_gen = (i for i in range(1000000))5、推导式中的循环和条件
- 推导式可以包含多个
for或if子句,以支持更复杂的逻辑。
# 过滤偶数并计算平方even_squares = [i * i for i in range(10) if i % 2 == 0]print(even_squares) # 输出: [0, 4, 16, 36, 64]
# 多层循环matrix = [[j for j in range(5)] for i in range(3)]print (matrix) # 输出: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]6、推导式与普通循环的性能对比
- 推导式通常比等价的普通循环代码执行得更快,因为 Python 在内部对推导式进行了优化。然而,对于非常简单的迭代,两者的性能差异可能不明显。
7、注意事项
- 推导式应该用于使代码更清晰、更简洁的情况。如果推导式过于复杂,可能会降低代码的可读性,这时应考虑使用普通的循环结构。
- 推导式中的变量作用域:在推导式中定义的变量(如循环变量)在推导式外部是不可见的。
拓展:使用列表推导式创建元组
- 列表推导式可以生成列表,然后我们可以使用
tuple()函数将这个列表转换为元组。这种方式在需要从推导式生成的数据创建元组时非常有用。
# 使用列表推导式生成列表,然后转换为元组my_tuple = tuple([x * 2 for x in range(5)])print(my_tuple) # 输出: (0, 2, 4, 6, 8)