0Pricing
DSA Interview Prep · 课时

函数、闭包与 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 列表、元组与切片
  2. Python 中的字典与集合
  3. 推导式与内置函数
  4. 函数、闭包与 Lambda
← 返回 DSA Interview Prep