杰瑞科技汇

函数嵌套怎么学?循序渐进的关键点是什么?

  1. 什么是函数嵌套? (基本概念)
  2. 为什么要使用函数嵌套? (核心优势)
  3. 如何使用函数嵌套? (语法和示例)
  4. 一个进阶概念:闭包 (函数嵌套的魔力)
  5. 常见的应用场景 (学以致用)
  6. 注意事项 (避坑指南)

什么是函数嵌套?

函数嵌套,顾名思义,就是在一个函数的内部定义另一个函数。

函数嵌套怎么学?循序渐进的关键点是什么?-图1
(图片来源网络,侵删)

就像俄罗斯套娃,一个函数里面套着另一个函数。

def outer_function():
    print("这是外部函数")
    def inner_function():
        print("这是内部函数")
    # 在外部函数内部,调用内部函数
    inner_function()
# 调用外部函数
outer_function()

输出结果:

这是外部函数
这是内部函数

关键点:

  • inner_function 只在 outer_function内部作用域中可见。
  • outer_function 外部,你无法直接调用 inner_function,尝试 inner_function() 会引发 NameError

为什么要使用函数嵌套?

这可能是你最重要的问题,直接把代码写在一个大函数里不就行了吗?为什么要嵌套?

函数嵌套怎么学?循序渐进的关键点是什么?-图2
(图片来源网络,侵删)

函数嵌套主要有三大优势:

数据隐藏 / 封装

这是最重要的原因,内部函数可以访问外部函数的变量,但这些变量对外部世界是不可见的,这可以避免全局命名空间的污染,保护数据不被意外修改。

想象一下,你写了一个复杂的计算器,里面有一个“辅助计算”的步骤,这个步骤的中间变量不应该被用户直接访问或修改,用函数嵌套就可以完美地封装它。

提高代码的可读性和组织性

当一个函数变得很长、很复杂时,我们可以把它拆分成几个逻辑上相关的小函数,并把它们嵌套在主函数内部,这样,主函数的结构会变得非常清晰,就像一个大纲,读者可以快速理解代码的流程,而不必被细节淹没。

实现闭包 (Closure)

这是函数嵌套最神奇、最强大的应用,闭包允许我们“外部函数作用域中的变量,即使外部函数已经执行完毕,这在创建“带有状态的函数”时非常有用,比如计数器或工厂函数。


如何使用函数嵌套?

让我们通过一个具体的例子来理解前两个优势。

示例:封装辅助计算

假设我们要写一个函数,计算一个列表中所有正数的平均值。

不使用嵌套的写法 (可能会污染全局命名空间):

# 一个辅助函数,只在这个特定场景下使用
def is_positive(number):
    return number > 0
def calculate_average_positive(numbers):
    positive_numbers = []
    for num in numbers:
        if is_positive(num): # 调用全局辅助函数
            positive_numbers.append(num)
    if not positive_numbers:
        return 0
    return sum(positive_numbers) / len(positive_numbers)
# is_positive 这个函数现在暴露在全局作用域中
# 虽然它很简单,但在大型项目中,这可能会与其他模块的函数名冲突

使用嵌套的写法 (更优雅、更安全):

def calculate_average_positive(numbers):
    # 辅助函数被封装在内部,外部世界无法访问它
    def is_positive(number):
        return number > 0
    positive_numbers = []
    for num in numbers:
        if is_positive(num): # 调用内部辅助函数
            positive_numbers.append(num)
    if not positive_numbers:
        return 0
    return sum(positive_numbers) / len(positive_numbers)
# 尝试在外部访问 is_positive 会报错
# is_positive(10)  # NameError: name 'is_positive' is not defined

在这个例子中,is_positive 函数的作用域被限制在了 calculate_average_positive 内部,它不会“污染”全局命名空间,代码组织也更好。


一个进阶概念:闭包

闭包是函数嵌套和作用域结合产生的“魔法”,它由两部分组成:

  1. 一个外部函数。
  2. 一个内部函数,该函数引用了外部函数的变量。

外部函数被调用并返回内部函数时,这个内部函数就形成了一个闭包,它会“它被创建时的环境,即外部函数的变量。

示例:创建一个简单的计数器

我们不使用全局变量,而是用闭包来创建一个计数器。

def make_counter():
    count = 0  # 外部函数的局部变量
    def counter():  # 内部函数,引用了外部的 count
        # nonlocal 关键字告诉 Python,我们要修改的是外层的 count 变量,而不是创建一个新的局部变量
        nonlocal count 
        count += 1
        return count
    # 返回内部函数 counter
    return counter
# 调用 make_counter,它会返回内部函数 counter
# my_counter 指向了 counter 函数,并且它“了 count = 0
my_counter = make_counter()
print(my_counter())  # 输出: 1 (count 变成了 1)
print(my_counter())  # 输出: 2 (count 变成了 2)
print(my_counter())  # 输出: 3 (count 变成了 3)
# 再创建一个独立的计数器,它有自己独立的 "count" 记忆
another_counter = make_counter()
print(another_counter()) # 输出: 1 (它的 count 从 0 开始)
print(another_counter()) # 输出: 2

闭包的魔力:

  • make_counter() 执行完毕后,按理说它内部的 count 变量应该被销毁。
  • 因为 my_counter 这个函数引用了 countcount 被保留了下来,没有被销毁。
  • nonlocal 关键字:当你在内部函数修改外部函数的变量时,必须使用 nonlocal,如果只是读取,可以不用,如果不加 nonlocal,Python 会认为你要在内部函数创建一个同名的新局部变量。

常见的应用场景

  1. 装饰器 这是闭包最经典、最重要的应用,装饰器是一个函数,它接受一个函数作为参数,返回一个新的函数,用于在不修改原函数代码的情况下,扩展其功能。

    def my_decorator(func):
        def wrapper():
            print("函数执行前...")
            func()  # 执行原始函数
            print("函数执行后...")
        return wrapper
    @my_decorator
    def say_hello():
        print("Hello, World!")
    say_hello()
    # 输出:
    # 函数执行前...
    # Hello, World!
    # 函数执行后...

    my_decorator 就是一个利用闭包原理的函数嵌套结构。

  2. 创建带状态的函数工厂 就像我们上面做的 make_counter 一样,你可以创建一个函数,它根据不同的参数,返回一系列功能相似但状态不同的函数。

  3. 事件处理器和回调 在 GUI 编程或异步编程中,你可能会定义一个嵌套函数作为回调,这个回调需要访问事件触发时的某些上下文信息(即外部函数的变量)。


注意事项

  1. 变量作用域要清晰:要明确区分局部变量、外部函数变量和全局变量,使用 nonlocalglobal 关键字时要小心,避免逻辑混乱。
  2. 不要过度嵌套:嵌套层次太深(比如超过2-3层)会让代码难以阅读和维护,如果函数变得太复杂,应该考虑将其拆分成多个独立的、非嵌套的函数。
  3. 性能影响:虽然现代 Python 解释器对闭包有很好的优化,但相比于非嵌套函数,闭包会带来轻微的性能开销,在绝大多数应用场景下,这点开销可以忽略不计,代码的清晰度和可维护性更重要。
特性 描述 优点
基本概念 在一个函数内部定义另一个函数。 代码结构清晰。
数据封装 内部函数可以访问外部变量,但外部变量对全局不可见。 避免命名空间污染,保护数据。
代码组织 将辅助函数或逻辑块放在主函数内部。 提高代码可读性,模块化。
闭包 返回的内部函数“了外部函数的变量。 创建带有状态的函数、实现装饰器等高级功能。

从理解基本概念开始,尝试用函数嵌套来组织你的代码,然后慢慢探索闭包的奇妙世界,你会发现,这是一个能让你的 Python 代码更专业、更强大的工具。

分享:
扫描分享到社交APP
上一篇
下一篇