在 Python 中,函数(Function)是封装特定逻辑的可重用代码块,用于实现单一、独立的功能。通过函数,我们可以将复杂代码分解为模块化的片段,提高代码的可读性、复用性和可维护性。本文将从基础到高级,详细介绍 Python 函数的核心特性和使用方法。

一、函数的基本定义与调用

1. 函数的定义

使用 def 关键字定义函数,语法格式如下:

def 函数名(参数列表):
    """函数文档字符串(可选,用于说明函数功能)"""
    函数体(实现功能的代码)
    return 返回值(可选)
  • 函数名:遵循标识符规则(字母、数字、下划线,不能以数字开头),建议使用小写字母 + 下划线(snake_case)命名,如 calculate_area
  • 参数列表:函数接收的输入(可选,无参数时留空)。
  • 文档字符串(docstring):用三引号包裹的说明文本,可通过 help(函数名) 查看,提升代码可读性。
  • 函数体:实现具体功能的代码块(需缩进)。
  • return 语句:指定函数的返回值(可选,无 return 时默认返回 None)。

2. 函数的调用

定义函数后,通过 函数名(参数) 调用,示例:

# 定义一个计算矩形面积的函数
def calculate_rect_area(length, width):
    """计算矩形面积
    参数:
        length: 矩形的长
        width: 矩形的宽
    返回:
        矩形的面积(长×宽)
    """
    area = length * width
    return area

# 调用函数
result = calculate_rect_area(5, 3)  # 传递参数 5(长)和 3(宽)
print(result)  # 输出:15(5×3的结果)

二、函数的参数类型

Python 函数的参数类型灵活多样,支持位置参数、关键字参数、默认参数、可变参数等,可根据需求灵活组合。

1. 位置参数(Positional Arguments)

最基础的参数类型,必须按定义的顺序传递,且数量与函数声明一致。

def greet(name, age):
    print(f"Hello, {name}! You are {age} years old.")

# 按位置传递参数(name=Alice, age=20)
greet("Alice", 20)  # 输出:Hello, Alice! You are 20 years old.

# 顺序错误会导致逻辑错误
greet(20, "Alice")  # 输出:Hello, 20! You are Alice years old.(错误)

2. 关键字参数(Keyword Arguments)

调用函数时通过 参数名=值 的形式传递,不依赖顺序,更清晰易读。

def greet(name, age):
    print(f"Hello, {name}! You are {age} years old.")

# 用关键字参数传递(顺序可任意)
greet(age=20, name="Alice")  # 输出:Hello, Alice! You are 20 years old.

混合使用:位置参数必须在关键字参数之前。

greet("Alice", age=20)  # 合法(位置参数在前,关键字参数在后)
# greet(age=20, "Alice")  # 报错(关键字参数不能在位置参数前)

3. 默认参数(Default Arguments)

定义函数时为参数指定默认值,调用时可省略该参数(使用默认值)。注意:默认参数必须放在位置参数之后。

def greet(name, age=18):  # age的默认值为18
    print(f"Hello, {name}! You are {age} years old.")

greet("Bob")  # 省略age,使用默认值18 → 输出:Hello, Bob! You are 18 years old.
greet("Bob", 22)  # 传递age=22 → 输出:Hello, Bob! You are 22 years old.

警告:默认参数的值若为可变对象(如列表、字典),可能导致意外行为(默认值在函数定义时初始化,而非每次调用时)。

def add_item(item, items=[]):  # 危险:默认值是可变列表
    items.append(item)
    return items

print(add_item("apple"))  # 第一次调用:['apple']
print(add_item("banana"))  # 第二次调用:['apple', 'banana'](意外保留了之前的结果)

正确做法:默认值设为 None,在函数体内初始化可变对象。

def add_item(item, items=None):
    if items is None:
        items = []  # 每次调用时重新初始化列表
    items.append(item)
    return items

print(add_item("apple"))  # ['apple']
print(add_item("banana"))  # ['banana'](符合预期)

4. 可变参数(Variable Arguments)

用于接收不确定数量的参数,分为两种:*args(接收位置参数)和 **kwargs(接收关键字参数)。

(1)*args:接收多个位置参数,返回元组

def sum_numbers(*args):  # *args 接收所有位置参数,打包为元组
    print("参数类型:", type(args))  # <class 'tuple'>
    total = 0
    for num in args:
        total += num
    return total

print(sum_numbers(1, 2, 3))  # 传递3个参数 → 6
print(sum_numbers(10, 20, 30, 40))  # 传递4个参数 → 100
print(sum_numbers())  # 传递0个参数 → 0

可结合位置参数使用(*args 必须在位置参数之后):

def print_info(title, *args):
    print(f"标题:{title}")
    print(f"内容:{args}")

print_info("成绩", 90, 85, 95)  # 标题:成绩;内容:(90, 85, 95)

(2)**kwargs:接收多个关键字参数,返回字典

def print_user(** kwargs):  # **kwargs 接收所有关键字参数,打包为字典
    print("参数类型:", type(kwargs))  # <class 'dict'>
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_user(name="Alice", age=20, gender="female")
# 输出:
# name: Alice
# age: 20
# gender: female

可结合其他参数使用(**kwargs 必须在所有参数最后):

def print_order(product, price, *args, **kwargs):
    print(f"商品:{product},价格:{price}")
    print(f"附加参数:{args}")
    print(f"其他信息:{kwargs}")

print_order("手机", 3999, "赠品:耳机", color="黑色", brand="华为")
# 输出:
# 商品:手机,价格:3999
# 附加参数:('赠品:耳机',)
# 其他信息:{'color': '黑色', 'brand': '华为'}

5. 强制关键字参数

通过在参数列表中添加 *,强制后续参数必须用关键字传递(避免位置参数顺序错误)。

def calculate(a, b, *, op="add"):  # * 之后的 op 必须用关键字传递
    if op == "add":
        return a + b
    elif op == "sub":
        return a - b

calculate(10, 5, op="add")  # 合法(op用关键字传递)→ 15
# calculate(10, 5, "add")  # 报错(op必须用关键字传递)

三、函数的返回值

函数通过 return 语句返回结果,特性如下:

1. 基本返回值

  • 一个函数可返回任意类型的值(数字、字符串、列表、字典、甚至函数)。
  • 执行到 return 语句后,函数立即终止,后续代码不再执行。
  • 无 return 语句时,默认返回 None
def is_positive(num):
    if num > 0:
        return True  # 返回布尔值
    else:
        return False

result = is_positive(5)
print(result)  # True

def empty_return():
    print("执行函数")
    return  # 等价于 return None

print(empty_return())  # 输出:执行函数 → None

2. 返回多个值

Python 函数可通过返回元组间接返回多个值,调用时可自动解包。

def get_user():
    name = "Alice"
    age = 20
    gender = "female"
    return name, age, gender  # 本质返回元组 (name, age, gender)

# 解包接收多个返回值
user_name, user_age, user_gender = get_user()
print(user_name, user_age, user_gender)  # Alice 20 female

四、函数的作用域(Scope)

作用域指变量的可访问范围,Python 中函数的作用域遵循LEGB 规则

  • L(Local):局部作用域(函数内部定义的变量)。
  • E(Enclosing):嵌套作用域(外层函数的变量,用于嵌套函数)。
  • G(Global):全局作用域(模块级定义的变量)。
  • B(Built-in):内置作用域(Python 内置函数 / 变量,如 printlen)。

1. 局部变量与全局变量

  • 局部变量:函数内部定义的变量,仅在函数内部可访问。
  • 全局变量:函数外部定义的变量,可在模块内的所有函数中访问(但修改需用 global 声明)。
# 全局变量
global_var = "我是全局变量"

def test_scope():
    # 局部变量
    local_var = "我是局部变量"
    print(local_var)  # 合法:访问局部变量
    print(global_var)  # 合法:访问全局变量

test_scope()
# print(local_var)  # 报错:局部变量在函数外不可访问

# 修改全局变量(需用 global 声明)
def modify_global():
    global global_var  # 声明要修改的全局变量
    global_var = "修改后的全局变量"

modify_global()
print(global_var)  # 输出:修改后的全局变量(已被修改)

2. 非局部变量(nonlocal)

用于嵌套函数中,修改外层函数(非全局)的变量。

def outer():
    outer_var = "外层变量"
    
    def inner():
        nonlocal outer_var  # 声明要修改外层函数的变量
        outer_var = "修改后的外层变量"
        print("inner中的outer_var:", outer_var)
    
    inner()
    print("outer中的outer_var:", outer_var)

outer()
# 输出:
# inner中的outer_var: 修改后的外层变量
# outer中的outer_var: 修改后的外层变量(被inner修改)

五、函数作为对象(高阶函数)

在 Python 中,函数是一等公民(First-Class Citizen),可作为变量赋值、作为参数传递、作为返回值,这是高阶函数的基础。

1. 函数赋值给变量

def add(a, b):
    return a + b

func = add  # 将函数赋值给变量
print(func(2, 3))  # 5(通过变量调用函数)

2. 函数作为参数传递

def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

# 高阶函数:接收函数作为参数
def calculate(func, a, b):
    return func(a, b)

print(calculate(add, 2, 3))  # 5(调用add)
print(calculate(multiply, 2, 3))  # 6(调用multiply)

Python 内置的 mapfilter 等函数就是典型的高阶函数:

# map:将函数应用于可迭代对象的每个元素
numbers = [1, 2, 3, 4]
result = map(lambda x: x * 2, numbers)  # 用lambda定义匿名函数
print(list(result))  # [2, 4, 6, 8]

3. 函数作为返回值

def make_adder(n):
    # 内部函数(闭包)
    def adder(x):
        return x + n
    return adder  # 返回内部函数

# 创建两个加法函数
add_5 = make_adder(5)  # 每次调用加5
add_10 = make_adder(10)  # 每次调用加10

print(add_5(3))  # 8(3+5)
print(add_10(3))  # 13(3+10)

六、匿名函数(lambda)

lambda 用于定义单行匿名函数,语法简洁,适合简单逻辑(复杂逻辑建议用 def 定义)。语法:lambda 参数: 表达式(表达式的结果即为返回值)。

# 用lambda定义加法函数(等价于 def add(a,b): return a+b)
add = lambda a, b: a + b
print(add(2, 3))  # 5

# 作为高阶函数的参数(常用场景)
numbers = [(1, 3), (4, 1), (2, 5)]
# 按元组的第二个元素排序
numbers.sort(key=lambda x: x[1])
print(numbers)  # [(4, 1), (1, 3), (2, 5)]

七、装饰器(Decorator)

装饰器是修改函数功能的工具,可在不修改原函数代码的前提下,为函数添加额外功能(如日志、计时、权限校验等)。本质是一个接收函数作为参数,并返回新函数的高阶函数

1. 基础装饰器

# 定义装饰器:为函数添加计时功能
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)  # 调用原函数
        end_time = time.time()
        print(f"{func.__name__} 执行时间:{end_time - start_time:.4f}秒")
        return result  # 返回原函数的结果
    return wrapper

# 使用装饰器(@语法糖)
@timer
def slow_function(seconds):
    time.sleep(seconds)  # 模拟耗时操作
    return "完成"

slow_function(1)  # 调用被装饰的函数
# 输出:slow_function 执行时间:1.0012秒(额外添加的计时功能)
  • @timer 等价于 slow_function = timer(slow_function)
  • wrapper(*args, **kwargs) 确保装饰器可适配任意参数的函数。

2. 带参数的装饰器

若装饰器需要自定义参数(如日志级别),需再嵌套一层函数。

def logger(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"[{level}] 调用函数:{func.__name__}")
            result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

# 使用带参数的装饰器
@logger(level="INFO")
def add(a, b):
    return a + b

add(2, 3)  # 输出:[INFO] 调用函数:add → 返回5

八、递归函数

递归函数是在函数体内调用自身的函数,适合解决可分解为 “重复子问题” 的场景(如阶乘、斐波那契数列、树遍历等)。递归需满足两个条件:-** 基线条件 :终止递归的条件(避免无限递归)。- 递归条件 **:函数调用自身,且问题规模缩小。

示例:计算阶乘

def factorial(n):
    # 基线条件:n=0或1时,阶乘为1
    if n == 0 or n == 1:
        return 1
    # 递归条件:n! = n × (n-1)!
    return n * factorial(n - 1)

print(factorial(5))  # 120(5×4×3×2×1)

注意:Python 递归深度有限制(默认约 1000 层),过深会导致 RecursionError。复杂场景建议用循环替代。

九、函数文档与注解

1. 文档字符串(docstring)

用三引号包裹的函数说明,可通过 help(函数名) 或 函数名.__doc__ 查看。

def area(radius):
    """计算圆的面积
    参数:
        radius: 圆的半径(正数)
    返回:
        圆的面积(π×radius²)
    异常:
        ValueError: 若radius为负数
    """
    if radius < 0:
        raise ValueError("半径不能为负数")
    return 3.14159 * radius**2

help(area)  # 查看文档

2. 函数注解(Function Annotations)

可选的类型提示,用于说明参数和返回值的预期类型(不影响代码执行,仅为提示)。

def add(a: int, b: int) -> int:  # 注解:a和b为int,返回值为int
    return a + b

add(2, 3)  # 符合注解
add("2", "3")  # 仍能执行(返回"23"),注解不强制类型检查

十、函数的最佳实践

  1. 单一职责:一个函数只做一件事,避免过大的函数体。
  2. 命名清晰:函数名应体现其功能(如 get_usercalculate_sum)。
  3. 参数不宜过多:参数过多会降低可读性,可考虑用字典或类封装。
  4. 避免全局变量:减少函数对全局变量的依赖,通过参数传递输入。
  5. 添加文档:复杂函数必须写 docstring,说明功能、参数、返回值。
  6. 处理异常:对可能出错的场景(如参数非法)添加异常处理。

总结

函数是 Python 编程的核心组件,通过封装逻辑实现代码复用。本文涵盖了函数的定义、参数类型(位置、关键字、默认、可变参数)、返回值、作用域、高阶函数、装饰器、递归等核心知识点。掌握这些内容,可编写更简洁、高效、可维护的 Python 代码。