函数、闭包与 Lambda
定义可复用的辅助函数,使用默认参数,并在面试题的排序和函数式模式中应用 lambda 表达式。
函数、闭包与 Lambda 是 CoddyKit 上的免费 DSA Interview Prep 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 DSA Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 DSA Interview Prep 课程共包含 4 节课。
在 Python 中定义函数
函数在 Python 中是一等公民:您可以传递函数、返回函数,也可以存储函数。命名清晰的辅助函数能让面试代码更易读。请先处理边界情况。
def is_palindrome(s: str) -> bool:
'''Return True if s reads the same forwards and backwards.'''
s = s.lower()
return s == s[::-1]
print(is_palindrome('Racecar')) # True
print(is_palindrome('hello')) # False
# Functions as values
checks = [is_palindrome]
print(checks[0]('level')) # True默认参数与关键字参数
默认值允许调用者省略参数。但绝不要使用可变的默认值,例如列表,因为所有调用者都会共享它。解决方法是将默认值设为 None,然后在函数内部创建列表。请查看代码。
# WRONG: shared mutable default
def bad_append(val, lst=[]):
lst.append(val)
return lst
print(bad_append(1)) # [1]
print(bad_append(2)) # [1, 2] surprise!
# CORRECT: use None sentinel
def good_append(val, lst=None):
if lst is None:
lst = []
lst.append(val)
return lst
print(good_append(1)) # [1]
print(good_append(2)) # [2]*args 和 **kwargs
*args 会将额外的位置参数收集到一个元组中;**kwargs 会将额外的关键字参数收集到一个字典中。调用函数时,* 还可以解包一个序列。
def total(*args):
return sum(args)
print(total(1, 2, 3)) # 6
print(total(1, 2, 3, 4)) # 10
# Unpack a list as positional args
point = [3, 7]
print(max(*point)) # 7
# **kwargs
def greet(**kwargs):
name = kwargs.get('name', 'World')
return f'Hello, {name}!'
print(greet(name='Alice')) # Hello, Alice!Lambda 表达式
lambda 是一种简短的单行函数:lambda params: expression。它非常适合在 sorted 或 max 中快速指定 key。对于更复杂的逻辑,使用有名称的 def 会更清晰。
# Lambda as sort key
pairs = [(1, 3), (2, 1), (3, 2)]
sorted_by_second = sorted(pairs, key=lambda p: p[1])
print(sorted_by_second) # [(2,1),(3,2),(1,3)]
# Lambda with map
double = list(map(lambda n: n * 2, [1, 2, 3]))
print(double) # [2, 4, 6]
# Named function is clearer for complex logic
def sort_key(p):
return (p[1], -p[0]) # secondary sort
print(sorted(pairs, key=sort_key))嵌套函数与闭包
闭包是一种能够记住外层函数变量的函数。在一个函数内部定义辅助函数后,它就可以使用外层变量,这对 DFS 很有帮助。
def make_counter(start=0):
count = [start] # list to allow mutation
def increment():
count[0] += 1
return count[0]
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
# Alternative: nonlocal keyword
def make_counter2():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment递归 DFS 中的闭包
一种常见模式是:在主函数内部定义一个 dfs 辅助函数,并让它将结果收集到外层变量中。这个内部函数对该作用域形成了闭包。
def max_depth(root):
'''Closure pattern for tree DFS.'''
max_d = [0] # mutable container for closure
def dfs(node, depth):
if node is None:
return
max_d[0] = max(max_d[0], depth)
dfs(node.left, depth + 1)
dfs(node.right, depth + 1)
dfs(root, 1)
return max_d[0]
# This pattern avoids a class or global state.使用 functools.lru_cache 进行记忆化
lru_cache(或 3.9 及更高版本中的 @cache)会根据函数参数记住函数结果,将朴素的斐波那契算法从 O(2^n) 优化为 O(n)。参数必须可哈希,因此请使用元组。
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
print(fib(40)) # 102334155 fast!
print(fib.cache_info()) # hits, misses, size高阶函数
高阶函数可以接收函数或返回函数。sorted、map 和 filter 都属于高阶函数。编写自己的高阶函数可以抽象重复模式。代码演示了 composition。
def apply_twice(f, x):
return f(f(x))
print(apply_twice(lambda n: n * 2, 3)) # 12 (3*2*2)
# Composing transformations
def compose(f, g):
return lambda x: f(g(x))
double = lambda n: n * 2
add_one = lambda n: n + 1
double_then_add = compose(add_one, double)
print(double_then_add(5)) # 11 ((5*2)+1)使用辅助函数进行递归
许多解决方案会将负责设置和处理边界情况的外层函数,与执行递归的内部辅助函数配合使用。这样既能保持公开接口简洁,又能隔离逻辑。请查看代码。
def flatten(nested):
'''Flatten an arbitrarily nested list.'''
result = []
def _flatten(lst):
for item in lst:
if isinstance(item, list):
_flatten(item)
else:
result.append(item)
_flatten(nested)
return result
print(flatten([1, [2, [3, 4], 5], 6]))
# [1, 2, 3, 4, 5, 6]使用 functools.partial 创建偏函数
functools.partial 会预先填充部分参数,并返回一个新的可调用对象。当接口要求无参数函数,而您需要固定一组参数时,这个工具非常有用。
from functools import partial
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
cube = partial(power, exp=3)
print(square(5)) # 25
print(cube(3)) # 27
print(list(map(square, [1, 2, 3, 4]))) # [1, 4, 9, 16]使用 Lambda 创建自定义比较器
Python 3 移除了双参数比较器,但 cmp_to_key 可以在复杂排序中重新提供这种能力,例如排列数字,使它们拼接后得到最大的数。请查看代码。
from functools import cmp_to_key
def largest_number(nums):
def compare(a, b):
if a + b > b + a: return -1
if a + b < b + a: return 1
return 0
strs = list(map(str, nums))
strs.sort(key=cmp_to_key(compare))
result = ''.join(strs)
return '0' if result[0] == '0' else result
print(largest_number([3, 30, 34, 5, 9])) # '9534330'快速测验
快速检查一下,现在展示您对函数、闭包和 lambda 的理解吧。深吸一口气,开始吧。🚀
课程回顾
回顾一下:绝不要使用可变默认值(请使用 None),闭包让内部辅助函数可以访问外层变量来执行 DFS,而 functools 中的 lru_cache 等工具可以支持高效解决方案。
常见问题解答
「函数、闭包与 Lambda」课时是免费的吗?
是的 — 「函数、闭包与 Lambda」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 DSA Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 DSA Interview Prep 课程共包含 4 节课。
「函数、闭包与 Lambda」这节课中我会学到什么?
定义可复用的辅助函数,使用默认参数,并在面试题的排序和函数式模式中应用 lambda 表达式。 你通过在浏览器中直接运行的动手代码来练习 DSA Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 DSA Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 DSA Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「函数、闭包与 Lambda」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 DSA Interview Prep 课中编写并运行代码吗?
能。每节 DSA Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 列表、元组与切片
- Python 中的字典与集合
- 推导式与内置函数
- 函数、闭包与 Lambda