一、函数是什么?

函数是一段组织好的、可重复使用的代码块,用于执行特定的任务。通过函数,我们可以:

  • 提高代码复用性:避免重复编写相同逻辑

  • 增强可读性:将复杂逻辑拆分为有意义的模块

  • 便于测试和维护:独立的功能单元更容易调试和修改

  • 实现抽象:隐藏实现细节,只暴露必要的接口

在Python中,函数是一等公民(First-class Citizen),这意味着函数可以像其他对象一样被赋值、传递和返回。


二、函数的定义与调用

2.1 基本语法

使用 def 关键字定义函数:

def function_name(parameters):
    """文档字符串(docstring)- 描述函数功能"""
    # 函数体
    # 执行逻辑
    return result  # 可选

一个完整的示例:

def calculate_bmi(weight_kg, height_m):
    """根据体重(公斤)和身高(米)计算BMI指数"""
    if height_m <= 0:
        raise ValueError("身高必须大于0")
    bmi = weight_kg / (height_m ** 2)
    return round(bmi, 2)

# 调用函数
bmi = calculate_bmi(70, 1.75)
print(f"您的BMI指数为:{bmi}")  # 输出:您的BMI指数为:22.86

2.2 文档字符串(Docstring)

良好的文档字符串是专业代码的标志:

def fetch_user_data(user_id, include_profile=True):
    """
    根据用户ID获取用户数据。
    
    Args:
        user_id (int): 用户的唯一标识符
        include_profile (bool): 是否包含用户详细资料,默认为True
    
    Returns:
        dict: 包含用户信息的字典,如果用户不存在则返回None
    
    Raises:
        ConnectionError: 当数据库连接失败时抛出
    """
    # 函数实现...
    pass

# 查看文档字符串
print(fetch_user_data.__doc__)
help(fetch_user_data)  # 更详细的帮助信息

三、参数传递深度解析

参数是函数与外部世界通信的主要方式,Python提供了极其灵活的参数系统。

3.1 位置参数(Positional Arguments)

最基本的参数传递方式,按位置匹配:

def create_user(name, age, email):
    return f"用户:{name},年龄:{age},邮箱:{email}"

# 必须按顺序传递
print(create_user("张三", 28, "zhangsan@example.com"))

3.2 默认参数(Default Arguments)

为参数提供默认值,使参数变为可选:

def create_user(name, age, email=None, is_active=True):
    return {
        'name': name,
        'age': age,
        'email': email or '未提供',
        'is_active': is_active
    }

# 可以只传递必填参数
user1 = create_user("李四", 25)
# 也可以覆盖默认值
user2 = create_user("王五", 30, "wangwu@example.com", False)

⚠️ 重要陷阱:默认参数是可变对象时

# ❌ 错误示例
def add_item(item, items=[]):  # 默认列表在函数定义时创建,所有调用共享
    items.append(item)
    return items

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2]  ← 意外结果!

# ✅ 正确做法
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item(1))  # [1]
print(add_item(2))  # [2]  ← 符合预期

3.3 关键字参数(Keyword Arguments)

通过参数名指定值,提高可读性且顺序无关:

def config_server(host, port, protocol='http', timeout=30):
    return f"{protocol}://{host}:{port} (timeout: {timeout}s)"

# 关键字参数调用
config = config_server(
    host='localhost',
    port=8080,
    timeout=60,
    protocol='https'
)

3.4 可变参数

*args - 接收任意数量的位置参数
def sum_numbers(*args):
    """计算任意数量数字的和"""
    total = 0
    for num in args:
        total += num
    return total

print(sum_numbers(1, 2, 3))        # 6
print(sum_numbers(10, 20, 30, 40)) # 100
print(sum_numbers())               # 0
**kwargs - 接收任意数量的关键字参数
def print_user_info(**kwargs):
    """打印用户信息,支持任意字段"""
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_user_info(name='Alice', age=25, city='New York', job='Engineer')
# 输出:
# name: Alice
# age: 25
# city: New York
# job: Engineer

3.5 参数组合顺序

当同时使用多种参数时,必须遵循以下顺序:

def complex_function(
    pos1,           # 1. 位置参数
    pos2,           # 2. 位置参数
    /,              # 3. 斜线之前必须是位置参数(Python 3.8+)
    kw_only1,       # 4. 关键字参数(在*之后)
    *args,          # 5. 可变位置参数
    kw_only2=None,  # 6. 关键字参数(带默认值)
    **kwargs        # 7. 可变关键字参数
):
    pass

位置参数(Positional-Only)示例:

def divide(a, b, /):
    """a和b只能通过位置传递"""
    return a / b

divide(10, 2)    # ✅ 正确
# divide(a=10, b=2)  # ❌ 错误:不能使用关键字参数

3.6 解包参数(Argument Unpacking)

使用 * 和 ** 将序列或字典解包为参数:

def greet(name, age):
    print(f"{name} is {age} years old")

# 列表解包
person = ["Alice", 30]
greet(*person)  # 等价于 greet("Alice", 30)

# 字典解包
info = {"name": "Bob", "age": 25}
greet(**info)   # 等价于 greet(name="Bob", age=25)

四、返回值

4.1 返回单个值

def square(x):
    return x ** 2

result = square(5)  # 25

4.2 返回多个值(实际上是元组)

def get_stats(numbers):
    return min(numbers), max(numbers), sum(numbers) / len(numbers)

min_val, max_val, avg = get_stats([1, 2, 3, 4, 5])
print(min_val, max_val, avg)  # 1 5 3.0

4.3 无返回值(返回None)

def log_message(msg):
    print(f"[LOG] {msg}")
    # 没有return语句,默认返回None

result = log_message("Test")
print(result)  # None

4.4 提前返回

def find_first_even(numbers):
    for num in numbers:
        if num % 2 == 0:
            return num  # 找到第一个偶数立即返回
    return None  # 未找到

print(find_first_even([1, 3, 5, 7]))  # None
print(find_first_even([1, 2, 3, 4]))  # 2

五、变量的作用域

5.1 LEGB规则

Python查找变量时遵循 LEGB 顺序:

  1. Local - 局部作用域(函数内部)

  2. Enclosing - 外层函数作用域

  3. Global - 全局作用域(模块级别)

  4. Built-in - 内置作用域(Python内置名称)

x = 'global'  # 全局变量

def outer():
    x = 'enclosing'  # 外层变量
    
    def inner():
        x = 'local'  # 局部变量
        print(x)     # 输出: local
    
    inner()
    print(x)         # 输出: enclosing

outer()
print(x)             # 输出: global

5.2 global 和 nonlocal

count = 0

def increment():
    global count  # 声明使用全局变量
    count += 1

increment()
print(count)  # 1

def outer():
    value = 0
    
    def inner():
        nonlocal value  # 修改外层函数的变量
        value += 1
        print(value)
    
    inner()  # 1
    inner()  # 2

outer()

六、总结

知识点 核心要点
函数定义 def + 函数名 + 参数 + 函数体 + return
参数类型 位置参数、默认参数、关键字参数、*args**kwargs
返回值 单个值、多个值(元组)、None
作用域 LEGB规则:Local → Enclosing → Global → Built-in
匿名函数 lambda 表达式,适合简单场景
最佳实践 单一职责、命名规范、类型注解、保持简洁

最后的话

函数是Python编程的基石。掌握函数的各种特性和用法,是写出高质量Python代码的关键。希望本文能帮助你系统性地理解和运用Python函数。

记住:好的函数不仅能让代码运行正确,更能让代码易于理解和维护。

持续学习,持续进步。如果觉得本文有帮助,欢迎分享给更多的Python爱好者!🐍

Logo

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

更多推荐