在前文探讨Python数据类型的基础上,今天我们继续学习python的函数、封包&解包、命名空间&作用域、高阶函数;本节将系统性地讲解函数的定义与使用、参数传递机制、命名空间与作用域规则,以及高阶函数等进阶特性。函数是结构化编程的核心构件,合理运用函数能够显著提升代码的模块化程度、可读性与可维护性。


一、 自定义函数

函数是一段组织良好、可重复使用的代码逻辑。定义函数即声明其名称、参数接口及执行体。

  • 基本语法

    def 函数名(参数1, 参数2):
        # 函数体
        结果 = 参数1 + 参数2
        return 结果
    
  • 函数调用与引用:函数名本身即指向函数对象的引用,可赋值给其他变量,从而实现间接调用。

    def plus(num):
        print(num + 1)
    
    plus(1)      # 输出 2
    func = plus  # 函数对象赋值
    func(1)      # 输出 2
    
  • 无参函数:不依赖外部输入,执行固定逻辑。

  • 带参函数:通过参数传递外部数据,决定函数的执行结果。

  • return 语句:用于返回函数执行结果。可返回单个值、表达式或多个值(实际以元组形式返回)。若未显式书写 return,函数默认返回 None。同时,return 会立即终止当前函数的执行流程。


二、 文档注释与类型标注

良好的函数应附带清晰的说明信息,便于他人理解与使用。

  • 文档注释(docstring):使用三引号字符串写在函数体开头,可通过 函数名.__doc__help(函数名) 查看。

    def my_abs(x):
        """返回数值 x 的绝对值。"""
        return x if x > 0 else -x
    
  • 类型标注(Type Hints):Python 3.5+ 支持在函数定义时标注参数和返回值的预期类型,旨在提升代码可读性并辅助IDE进行静态检查,但不会在运行时强制类型约束。

    from typing import List
    
    def greet(names: List[str]) -> str:
        return "Hello, " + " ".join(names)
    

三、 参数传递机制

参数传递是函数调用的核心环节,理解其规则有助于编写健壮的接口。

  1. 可变对象与不可变对象

    • 传递不可变对象(如 intstrtuple)时,函数内部对形参的修改不会影响外部实参,因为传递的是值的副本(实际是引用的副本,但因对象不可变,修改会生成新对象)。
    • 传递可变对象(如 listdict)时,函数内部通过形参引用的修改会直接影响外部对象,需谨慎处理。
  2. 参数分类

    • 必需参数(位置参数):调用时须按定义顺序传入,数量必须匹配。
    • 关键字参数:以 形参名=值 形式传入,顺序可调换。
    • 默认参数:在定义时为形参赋予默认值,调用时可省略。
    • 不定长参数
      • *args:接收任意数量的位置参数,打包为元组
      • **kwargs:接收任意数量的关键字参数,打包为字典
      def func(a, b, *args, **kwargs):
          print(a, b, args, kwargs)
      
      func(1, 2, 3, 4, name="Tom", age=18)
      # 输出: 1 2 (3, 4) {'name': 'Tom', 'age': 18}
      
  3. 特殊分隔符 /*

    • / 之前的参数为仅限位置参数,不能以关键字形式传入。
    • * 之后的参数为仅限关键字参数,必须以关键字形式传入。
    • 此机制有助于强制调用规范,提高接口清晰度。

四、 匿名函数(lambda)

lambda 表达式用于创建简短的匿名函数,通常用于需要一次性简单逻辑的场景,如作为高阶函数的参数。

# 常规函数
def add(x, y):
    return x + y

# lambda 等效形式
lambda x, y: x + y

# 作为排序 key 使用
print(sorted([-4, 2, 3], key=lambda x: abs(x)))  # 按绝对值排序

五、 封包与解包

  • 封包(Packing):Python 自动将多个值组合为元组。

    a = 1, 2, 3   # a 为 (1, 2, 3)
    
  • 解包(Unpacking):将可迭代对象中的元素分配给多个变量。

    • 赋值解包:支持 * 收集剩余元素。
      a, *b, c = (1, 2, 3, 4, 5)
      print(a, b, c)  # 1 [2, 3, 4] 5
      
    • 函数调用解包
      • * 作用于列表/元组,将其展开为位置参数。
      • ** 作用于字典,将其展开为关键字参数。
      def func(a, b, c):
          print(a, b, c)
      
      my_list = [1, 2, 3]
      func(*my_list)        # 等效于 func(1, 2, 3)
      
      my_dict = {'a': 1, 'b': 2, 'c': 3}
      func(**my_dict)       # 等效于 func(a=1, b=2, c=3)
      

六、 命名空间与作用域

命名空间是名称(变量、函数等)到对象的映射。作用域决定了名称在代码中的可访问范围。

  • LEGB 查找顺序

    1. Local:函数内部局部作用域。
    2. Enclosing:外层嵌套函数的作用域(闭包环境)。
    3. Global:当前模块的全局作用域。
    4. Built-in:Python 内置名称作用域。
  • globalnonlocal 声明

    • 在函数内部若需修改全局变量,须使用 global 声明。
    • 在嵌套函数中若需修改外层函数的变量,须使用 nonlocal 声明。

P.S.:在函数内对变量进行赋值操作时,Python 默认将其视为局部变量。若在赋值前尝试访问该变量,会引发“引用前赋值”错误。

a = 1
def func():
    print(a)   # 报错:UnboundLocalError
    a = 2

七、 常用高阶函数

高阶函数指以函数作为参数或返回值的函数,是函数式编程的重要工具。

  1. filter:根据条件筛选可迭代对象中的元素。

    result = filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5])
    print(list(result))  # [2, 4]
    
  2. map:对可迭代对象中的每个元素应用同一函数。

    result = map(lambda x: x**2, [1, 2, 3])
    print(list(result))  # [1, 4, 9]
    
  3. reduce:对可迭代对象中的元素进行累积操作(Python 3 中位于 functools 模块)。

    from functools import reduce
    
    result = reduce(lambda x, y: x + y, range(1, 101))
    print(result)  # 5050
    

八、 递归函数

递归函数指在函数体内部调用自身。它适用于可分解为相同子问题的场景,如树结构遍历、分治算法等。

  • 关键要素

    1. 递归边界(终止条件):明确何时停止递归。
    2. 递归递推(缩小问题规模):将当前问题转化为规模更小的同类问题。
  • 注意事项

    • Python 默认递归深度限制为 1000,过深递归会引发 RecursionError
    • 未优化的递归可能包含大量重复计算,可通过记忆化(缓存已计算结果)提升效率。
    • 在实际工程中,循环实现通常具有更好的性能和可控性,仅在处理天然递归结构(如文件目录、树形数据)时,递归才显现其表达优势。

收尾:

以上内容涵盖了Python函数的基础定义到高阶抽象。初学阶段可能对参数传递、作用域规则等感到抽象,建议通过实际编码逐步加深理解。

下图为本文知识点的思维导图(示意):
在这里插入图片描述

祝编码顺利。如有疑问,欢迎进一步探讨。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐