📦 14. 函数详解

本章概述

学习 Python 函数的定义、参数、返回值、作用域等。 预计学习时间:70 分钟


14.1 函数定义

函数就是一段可以重复使用的代码块。

基本格式

def 函数名(参数列表):
    """函数文档字符串"""
    函数体
    return 返回值

示例

def greet(name):
    """打招呼的函数"""
    print(f"你好,{name}!")
 
# 调用函数
greet("Tom")    # 你好,Tom!
greet("Jerry")  # 你好,Jerry!

函数命名规范

  • 小写字母 + 下划线(蛇形命名)
  • 动词开头,见名知意
  • 例如:get_name(), calculate_sum(), print_info()

14.2 函数参数

位置参数

按顺序传递参数:

def add(a, b):
    return a + b
 
add(3, 5)  # 8(a=3, b=5)

关键字参数

按参数名传递,可以不按顺序:

def greet(name, age):
    print(f"我叫{name},今年{age}岁")
 
greet(age=18, name="Tom")  # 可以不按顺序

位置参数和关键字参数可以混合用

但位置参数必须在关键字参数前面:

greet("Tom", age=18)  # ✅ 可以
# greet(name="Tom", 18)  # ❌ 不行

默认参数

参数有默认值,调用时可以不传:

def greet(name, greeting="你好"):
    print(f"{greeting}{name}!")
 
greet("Tom")           # 你好,Tom!
greet("Tom", "Hello")  # Hello,Tom!
greet("Tom", greeting="Hi")  # Hi,Tom!

默认参数的坑

默认参数不要用可变对象!

# ❌ 错误:默认参数用了可变对象
def add_item(item, lst=[]):
    lst.append(item)
    return lst
 
print(add_item(1))  # [1]
print(add_item(2))  # [1, 2](不是[2]!因为默认列表被复用了)
 
# ✅ 正确:用None当默认值
def add_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

可变参数 *args

接收任意多个位置参数,变成一个元组:

def sum_all(*args):
    print(args)  # args是一个元组
    total = 0
    for n in args:
        total += n
    return total
 
sum_all(1, 2, 3)        # 6
sum_all(1, 2, 3, 4, 5)  # 15

关键字可变参数 **kwargs

接收任意多个关键字参数,变成一个字典:

def print_info(**kwargs):
    print(kwargs)  # kwargs是一个字典
    for key, value in kwargs.items():
        print(f"{key}: {value}")
 
print_info(name="Tom", age=18, gender="男")

参数组合

参数定义的顺序必须是:

  1. 位置参数
  2. 默认参数
  3. *args
  4. 命名关键字参数
  5. **kwargs
def func(a, b, c=0, *args, d=1, **kwargs):
    print(a, b, c, args, d, kwargs)

什么时候用 *args / **kwargs?

  • 参数数量不确定时
  • 写装饰器时
  • 传递参数给另一个函数时

14.3 返回值 return

基本用法

def add(a, b):
    return a + b
 
result = add(3, 5)
print(result)  # 8

返回多个值

Python 特色

Python 函数可以返回多个值,本质是返回一个元组。

def calc(a, b):
    return a + b, a - b, a * b
 
sum_val, diff, product = calc(10, 3)
print(sum_val, diff, product)  # 13 7 30

没有 return

函数没有 return 时,默认返回 None

def say_hello():
    print("hello")
 
result = say_hello()
print(result)  # None

多个 return

函数可以有多个 return,遇到第一个 return 就返回:

def get_grade(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 60:
        return "C"
    else:
        return "D"

14.4 函数文档字符串 docstring

函数开头的字符串,用来说明函数的用途、参数、返回值等。

def add(a, b):
    """
    计算两个数的和
 
    参数:
        a (int/float): 第一个数
        b (int/float): 第二个数
 
    返回:
        int/float: 两个数的和
 
    示例:
        >>> add(3, 5)
        8
    """
    return a + b

查看文档字符串

help(add)       # 查看帮助信息
print(add.__doc__)  # 直接看文档字符串

好习惯

重要的函数一定要写文档字符串,方便自己和别人理解。


14.5 变量作用域

局部变量 vs 全局变量

x = 10  # 全局变量
 
def func():
    y = 20  # 局部变量,只能在函数内用
    print(x)  # 可以读全局变量
    print(y)
 
func()
# print(y)  # ❌ 报错,y是局部变量,外面访问不到

global 关键字

在函数内修改全局变量,需要用 global 声明:

x = 10
 
def func():
    global x  # 声明x是全局变量
    x = 20    # 修改全局变量
 
func()
print(x)  # 20

尽量少用 global

全局变量会让代码难以理解和维护,尽量通过参数和返回值传递数据。

nonlocal 关键字

在嵌套函数中修改外层函数的变量:

def outer():
    x = 10
 
    def inner():
        nonlocal x  # 声明x是外层函数的变量
        x = 20
 
    inner()
    print(x)  # 20
 
outer()

14.6 嵌套函数(内部函数)

函数里面还可以定义函数:

def outer():
    print("外层函数")
 
    def inner():
        print("内层函数")
 
    inner()  # 调用内层函数
 
outer()

闭包

内部函数引用了外部函数的变量,外部函数返回内部函数:

def make_adder(n):
    def adder(x):
        return x + n  # 引用了外部的n
    return adder  # 返回内部函数
 
add5 = make_adder(5)
print(add5(3))   # 8
print(add5(10))  # 15
 
add10 = make_adder(10)
print(add10(3))  # 13

什么是闭包?

内部函数 + 它引用的外部变量 = 闭包 闭包可以记住外部函数的状态。


14.7 匿名函数 lambda

简单的函数可以用 lambda 一行写完。

格式

lambda 参数列表: 表达式

示例

# 普通函数
def add(a, b):
    return a + b
 
# lambda 等价写法
add = lambda a, b: a + b
 
print(add(3, 5))  # 8

lambda 的使用场景

通常用在需要一个简单函数,且只用一次的地方:

# 1. 作为排序的key
words = ["apple", "banana", "cherry", "date"]
words.sort(key=lambda x: len(x))  # 按长度排序
 
# 2. 作为map/filter的参数
nums = [1, 2, 3, 4, 5]
result = map(lambda x: x * 2, nums)
 
# 3. 作为回调函数

lambda 的限制

  • 只能有一个表达式
  • 不能写复杂逻辑
  • 太复杂的逻辑还是用 def 定义函数

14.8 高阶函数

接收函数作为参数,或者返回函数的函数。

map()

对序列每个元素应用函数,返回新序列:

nums = [1, 2, 3, 4, 5]
 
# 每个元素乘2
result = map(lambda x: x * 2, nums)
print(list(result))  # [2, 4, 6, 8, 10]

filter()

过滤序列,保留满足条件的元素:

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# 保留偶数
result = filter(lambda x: x % 2 == 0, nums)
print(list(result))  # [2, 4, 6, 8, 10]

reduce()

把序列累积成一个值(需要 import):

from functools import reduce
 
nums = [1, 2, 3, 4, 5]
 
# 求和
result = reduce(lambda a, b: a + b, nums)
print(result)  # 15

更 Pythonic 的方式

大多数情况下,用列表推导式比 map/filter 更清晰:

# map
result = [x * 2 for x in nums]
 
# filter
result = [x for x in nums if x % 2 == 0]

14.9 递归函数

函数自己调用自己。

示例:阶乘

def factorial(n):
    if n == 0 or n == 1:
        return 1  # 递归出口(必须有!)
    return n * factorial(n - 1)
 
print(factorial(5))  # 120

递归的三要素

  1. 递归出口:什么时候停止递归(必须有,否则死循环)
  2. 递归公式:怎么把大问题拆成小问题
  3. 返回值:每次递归返回什么

注意递归深度

Python 默认递归深度约 1000 层,太深会报错。 能用循环实现的尽量用循环,效率更高。


🔗 相关章节


📝 我的笔记

在这里记录你的理解、疑问和练习代码

# 你的练习代码
 

✅ 本章检查清单

  • 掌握函数的定义和调用
  • 掌握各种参数类型(位置、默认、*args、**kwargs)
  • 理解返回值和多返回值
  • 会写函数文档字符串
  • 理解变量作用域(局部/全局)
  • 了解嵌套函数和闭包
  • 会使用 lambda 匿名函数
  • 了解 map/filter/reduce 高阶函数
  • 理解递归函数