lambda 与作用域
本节目标
学完这一节,你会知道:
lambda是什么,适合在什么时候使用- 如何用
lambda配合sorted()排序 - 什么是变量作用域
- Python 查找变量的 LEGB 规则
global和nonlocal为什么要谨慎使用
这一节有两个主题:一个是短小函数的写法,一个是变量在哪里能被看见。
先跑一个例子
新建文件 lambda_scope_demo.py,写入:
students = [
{"name": "小明", "score": 85},
{"name": "小红", "score": 92},
{"name": "小刚", "score": 78},
]
students.sort(key=lambda student: student["score"])
for student in students:
print(f"{student['name']}:{student['score']}")
运行:
python3 lambda_scope_demo.py
你会看到学生按分数从低到高输出。
这里的 lambda student: student["score"] 是一个短小的匿名函数。
什么是 lambda?
普通函数:
def add(a, b):
return a + b
lambda 写法:
add = lambda a, b: a + b
调用方式一样:
print(add(3, 5))
lambda 的语法是:
lambda 参数: 返回值
它适合非常简单、临时使用的小函数。
lambda 最常用场景:排序 key
按年龄排序:
users = [
{"name": "张三", "age": 28},
{"name": "李四", "age": 22},
{"name": "王五", "age": 35},
]
users.sort(key=lambda user: user["age"])
print(users)
按字符串长度排序:
words = ["Python", "Go", "JavaScript", "C"]
words.sort(key=lambda word: len(word))
print(words)
key 需要一个函数,告诉 Python 用什么作为排序依据。lambda 正好适合这种短小函数。
lambda 不适合复杂逻辑
不推荐:
result = lambda score: "优秀" if score >= 90 else "及格" if score >= 60 else "不及格"
更清楚的写法:
def get_level(score):
if score >= 90:
return "优秀"
elif score >= 60:
return "及格"
else:
return "不及格"
lambda 的原则:一眼能看懂就用,看不懂就写普通函数。
什么是作用域?
作用域就是变量能被访问的范围。
def say_hi():
message = "你好"
print(message)
say_hi()
print(message)
最后一行会报 NameError,因为 message 是函数内部变量,函数外看不见。
Local:局部作用域
函数内部创建的变量是局部变量。
def demo():
x = 10
print(x)
demo()
x 只能在 demo() 里面使用。
Global:全局作用域
写在函数外的变量是全局变量。
site_name = "马哥的python小屋"
def show_site():
print(site_name)
show_site()
函数内部可以读取全局变量。
但不建议随便在函数里修改全局变量。更推荐通过参数和返回值传递数据。
LEGB 规则
Python 查找变量时,大致按这个顺序:
- Local:当前函数内部
- Enclosing:外层函数
- Global:当前文件的全局变量
- Built-in:内置名字,比如
print、len
例子:
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x)
inner()
outer()
输出:
local
因为 Python 先找到了当前函数里的 x。
global
如果确实要在函数里修改全局变量,可以用 global:
count = 0
def add_one():
global count
count += 1
add_one()
print(count)
global 能用,但要谨慎。全局变量改来改去,程序大了以后容易出 bug。
nonlocal
nonlocal 用来修改外层函数里的变量。
def make_counter():
count = 0
def add_one():
nonlocal count
count += 1
return count
return add_one
counter = make_counter()
print(counter())
print(counter())
输出:
1
2
这里 count 不在 add_one() 里面,也不是全局变量,而是在外层函数 make_counter() 里。
逐行拆解
再看开头的排序代码:
students.sort(key=lambda student: student["score"])
sort() 会遍历每个学生字典。
lambda student: student["score"] 告诉它:排序时使用每个学生的 score。
for student in students:
这里的 student 是循环变量,只在当前循环逻辑中使用。
自己改一改
把 lambda_scope_demo.py 改成按名字长度排序:
names = ["Python", "Go", "JavaScript", "C"]
names.sort(key=lambda name: len(name))
print(names)
然后继续改:
- 改成按字符串最后一个字符排序
- 改成按分数从高到低排序,提示:
reverse=True - 把 lambda 改成普通函数再试一次
常见错误
1. lambda 写太复杂
lambda 适合简单表达式,不适合写很多判断和循环。复杂逻辑请用普通函数。
2. 函数外访问局部变量
def demo():
name = "小明"
print(name)
这会报 NameError。
3. 在函数里修改全局变量但没写 global
count = 0
def add_one():
count += 1
这会报 UnboundLocalError。更推荐写成:
def add_one(count):
return count + 1
4. 覆盖内置名字
不要这样写:
list = [1, 2, 3]
list 是 Python 内置名字。覆盖后,后面想用 list() 就容易出问题。
小练习
练习 1:按价格排序
给定商品列表:
products = [
{"name": "苹果", "price": 3.5},
{"name": "香蕉", "price": 2.8},
{"name": "橙子", "price": 4.2},
]
用 lambda 按价格从低到高排序。
练习 2:局部变量观察
定义函数,在函数内部创建变量并打印。然后试着在函数外打印这个变量,观察报错。
练习 3:计数器
用普通函数写一个 add_one(count),接收数字并返回加 1 后的结果。
参考答案
练习 1:
products = [
{"name": "苹果", "price": 3.5},
{"name": "香蕉", "price": 2.8},
{"name": "橙子", "price": 4.2},
]
products.sort(key=lambda product: product["price"])
print(products)
练习 2:
def demo():
message = "函数内部变量"
print(message)
demo()
# print(message) # 取消注释会报 NameError
练习 3:
def add_one(count):
return count + 1
count = 0
count = add_one(count)
count = add_one(count)
print(count)
小结
这一节你学会了:
lambda可以创建短小的匿名函数lambda常用于sorted()和list.sort()的key- 作用域决定变量在哪里能被访问
- Python 按 LEGB 顺序查找变量
global和nonlocal能修改外层变量,但要谨慎使用
下一节我们会学习装饰器。它可以在不修改原函数代码的情况下,给函数增加新功能。
短函数和变量地盘,慢慢就能分清
lambda 适合短平快,作用域负责告诉变量能在哪儿被看见。这里有一点抽象,马哥建议别硬背 LEGB,先多跑几个函数内外变量的例子。等你遇到 NameError 时,会突然想起:原来它只是找不到家。
还没有评论,来抢沙发吧!