Skip to content

Python 函数

函数是组织好的、可重复使用的代码块,用于实现单一或相关联的功能。

1. 函数的定义与调用

1.1 基本语法

python
# 使用 def 关键字定义函数
def greet(name):
    """向指定的人打招呼(这是文档字符串)"""
    print(f"你好,{name}!")

# 调用函数
greet("Alice")   # 你好,Alice!
greet("Bob")     # 你好,Bob!

# 查看文档字符串
print(greet.__doc__)  # 向指定的人打招呼(这是文档字符串)
help(greet)

1.2 返回值

python
# return 返回结果,函数遇到 return 立即结束
def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 8

# 没有 return 或 return 后无值,返回 None
def say_hello():
    print("Hello")

result = say_hello()  # Hello
print(result)          # None

# 返回多个值(本质是返回元组)
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([3, 1, 4, 1, 5, 9])
print(low, high)  # 1 9

result = min_max([3, 1, 4, 1, 5, 9])
print(result)       # (1, 9)
print(type(result)) # <class 'tuple'>

1.3 空函数

python
# 用 pass 占位,后续再实现
def todo():
    pass

# 或者用 ... (Ellipsis)
def not_implemented():
    ...

2. 函数参数

2.1 位置参数

python
# 按位置一一对应传递
def power(base, exp):
    return base ** exp

print(power(2, 10))   # 1024
print(power(10, 2))   # 100  (顺序不同,结果不同)

2.2 默认参数

python
# 参数可以设置默认值,调用时可省略
def greet(name, greeting="你好"):
    print(f"{greeting}{name}!")

greet("Alice")              # 你好,Alice!
greet("Bob", "Hello")       # Hello,Bob!
greet("Charlie", greeting="早上好")  # 早上好,Charlie!

# ⚠️ 默认参数必须放在非默认参数后面
# def bad(a=1, b):  # ❌ SyntaxError
#     pass

# ⚠️ 默认参数陷阱:可变对象作为默认值
def append_to(item, lst=[]):
    lst.append(item)
    return lst

print(append_to(1))   # [1]
print(append_to(2))   # [1, 2]  (不是 [2]!默认列表被共享了)
print(append_to(3))   # [1, 2, 3]

# ✅ 正确做法:用 None 作为默认值
def append_to_safe(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

print(append_to_safe(1))   # [1]
print(append_to_safe(2))   # [2]  ✅

2.3 可变参数(*args

python
# *args 接收任意数量的位置参数,打包为元组
def total(*args):
    print(type(args))  # <class 'tuple'>
    return sum(args)

print(total(1, 2, 3))       # 6
print(total(1, 2, 3, 4, 5)) # 15
print(total())               # 0

# 实际应用:计算平均值
def average(*nums):
    if not nums:
        return 0
    return sum(nums) / len(nums)

print(average(80, 90, 85))  # 85.0

# 混合使用:普通参数 + *args
def introduce(name, *hobbies):
    print(f"{name} 的爱好:{', '.join(hobbies)}")

introduce("Alice", "读书", "跑步", "编程")
# Alice 的爱好:读书, 跑步, 编程

2.4 关键字可变参数(**kwargs

python
# **kwargs 接收任意数量的关键字参数,打包为字典
def print_info(**kwargs):
    print(type(kwargs))  # <class 'dict'>
    for key, value in kwargs.items():
        print(f"  {key}: {value}")

print_info(name="Alice", age=25, city="Beijing")
#   name: Alice
#   age: 25
#   city: Beijing

# 混合使用
def create_user(name, age, **extra):
    user = {"name": name, "age": age}
    user.update(extra)
    return user

user = create_user("Bob", 30, email="bob@test.com", phone="123")
print(user)
# {'name': 'Bob', 'age': 30, 'email': 'bob@test.com', 'phone': '123'}

2.5 参数组合与顺序

python
# 参数顺序:位置参数 → 默认参数 → *args → 关键字参数 → **kwargs
def func(a, b=10, *args, key="default", **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"key={key}")
    print(f"kwargs={kwargs}")

func(1, 2, 3, 4, key="hello", x=100, y=200)
# a=1, b=2
# args=(3, 4)
# key=hello
# kwargs={'x': 100, 'y': 200}

# 万能参数签名
def universal(*args, **kwargs):
    print(f"位置参数:{args}")
    print(f"关键字参数:{kwargs}")

universal(1, 2, 3, name="Alice", age=25)
# 位置参数:(1, 2, 3)
# 关键字参数:{'name': 'Alice', 'age': 25}

2.6 仅限关键字参数与仅限位置参数

python
# * 后面的参数必须用关键字传递(仅限关键字参数)
def connect(host, port, *, timeout=30, retry=3):
    print(f"连接 {host}:{port},超时={timeout}s,重试={retry}次")

connect("localhost", 8080)                    # ✅
connect("localhost", 8080, timeout=10)        # ✅
# connect("localhost", 8080, 10)              # ❌ TypeError

# / 前面的参数必须用位置传递(仅限位置参数,Python 3.8+)
def calc(x, y, /, *, op="add"):
    if op == "add":
        return x + y
    return x - y

print(calc(3, 5))              # 8  ✅
print(calc(3, 5, op="sub"))    # -2 ✅
# print(calc(x=3, y=5))        # ❌ TypeError

# 组合使用:/ 左边仅位置,/ 和 * 之间可选,* 右边仅关键字
def example(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

example(1, 2, 3, d=4, e=5, f=6)  # 1 2 3 4 5 6

2.7 参数解包

python
# * 解包序列为位置参数
def add(a, b, c):
    return a + b + c

nums = [1, 2, 3]
print(add(*nums))     # 6   等价于 add(1, 2, 3)

t = (10, 20, 30)
print(add(*t))         # 60

# ** 解包字典为关键字参数
info = {"a": 100, "b": 200, "c": 300}
print(add(**info))     # 600  等价于 add(a=100, b=200, c=300)

# 混合解包
def greet(name, greeting, punctuation):
    print(f"{greeting}{name}{punctuation}")

args = ("Alice",)
kwargs = {"greeting": "你好", "punctuation": "!"}
greet(*args, **kwargs)  # 你好,Alice!

3. 作用域与闭包

3.1 LEGB 规则

Python 查找变量的顺序:Local → Enclosing → Global → Built-in

python
x = "全局变量"  # Global

def outer():
    x = "外层函数变量"  # Enclosing

    def inner():
        x = "内层函数变量"  # Local
        print(x)

    inner()
    print(x)

outer()
print(x)
# 内层函数变量
# 外层函数变量
# 全局变量

3.2 global 和 nonlocal

python
# global —— 在函数内修改全局变量
count = 0

def increment():
    global count
    count += 1

increment()
increment()
print(count)  # 2

# nonlocal —— 在内层函数中修改外层函数的变量
def counter():
    n = 0
    def increment():
        nonlocal n
        n += 1
        return n
    return increment

c = counter()
print(c())  # 1
print(c())  # 2
print(c())  # 3

3.3 闭包

闭包 = 内层函数 + 引用的外层变量。外层函数返回后,内层函数仍然可以访问外层的变量。

python
# 基本闭包
def make_multiplier(factor):
    def multiply(x):
        return x * factor  # factor 被"记住"了
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15
print(double(10))  # 20

# 查看闭包引用的变量
print(double.__closure__[0].cell_contents)  # 2

# 实用示例:生成问候函数
def make_greeter(greeting):
    def greet(name):
        return f"{greeting}{name}!"
    return greet

chinese_greet = make_greeter("你好")
english_greet = make_greeter("Hello")

print(chinese_greet("Alice"))   # 你好,Alice!
print(english_greet("Alice"))   # Hello,Alice!

# ⚠️ 闭包常见陷阱:循环中的闭包
funcs = []
for i in range(3):
    funcs.append(lambda: i)  # 所有 lambda 共享同一个变量 i

print([f() for f in funcs])  # [2, 2, 2]  (不是 [0, 1, 2]!)

# ✅ 解决方法:用默认参数捕获当前值
funcs = []
for i in range(3):
    funcs.append(lambda x=i: x)

print([f() for f in funcs])  # [0, 1, 2]  ✅

4. 匿名函数 lambda

lambda 用于创建简短的、一次性的小函数。

python
# 语法:lambda 参数: 表达式
add = lambda a, b: a + b
print(add(3, 5))  # 8

# 等价于
def add(a, b):
    return a + b

# lambda 只能包含单个表达式,不能有语句(if/for/while 等)
# 但可以使用条件表达式
check = lambda x: "偶数" if x % 2 == 0 else "奇数"
print(check(4))  # 偶数
print(check(7))  # 奇数

# lambda 最常见的用途:作为高阶函数的参数

# 排序:按绝对值排序
nums = [-5, 3, -2, 8, -1]
print(sorted(nums, key=lambda x: abs(x)))
# [-1, -2, 3, -5, 8]

# 排序:按字典的某个字段
students = [
    {"name": "Alice", "score": 90},
    {"name": "Bob", "score": 85},
    {"name": "Charlie", "score": 92},
]
students.sort(key=lambda s: s["score"], reverse=True)
for s in students:
    print(f"{s['name']}: {s['score']}")
# Charlie: 92
# Alice: 90
# Bob: 85

# 多条件排序
data = [("Alice", 90), ("Bob", 90), ("Charlie", 85)]
data.sort(key=lambda x: (-x[1], x[0]))  # 分数降序,同分按姓名升序
print(data)
# [('Alice', 90), ('Bob', 90), ('Charlie', 85)]

5. 高阶函数

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

5.1 map()

对可迭代对象的每个元素应用函数,返回迭代器。

python
# 基本用法
nums = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, nums))
print(squares)  # [1, 4, 9, 16, 25]

# 等价于列表推导式
squares = [x ** 2 for x in nums]

# 使用已有函数
words = ["hello", "world", "python"]
upper_words = list(map(str.upper, words))
print(upper_words)  # ['HELLO', 'WORLD', 'PYTHON']

# 多个可迭代对象
a = [1, 2, 3]
b = [10, 20, 30]
result = list(map(lambda x, y: x + y, a, b))
print(result)  # [11, 22, 33]

# 类型转换
str_nums = ["1", "2", "3", "4"]
int_nums = list(map(int, str_nums))
print(int_nums)  # [1, 2, 3, 4]

5.2 filter()

过滤可迭代对象,保留使函数返回 True 的元素。

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

# 等价于列表推导式
evens = [x for x in nums if x % 2 == 0]

# 过滤空字符串
words = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, words))  # None 表示过滤假值
print(non_empty)  # ['hello', 'world', 'python']

# 过滤字典列表
users = [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 17},
    {"name": "Charlie", "age": 30},
    {"name": "David", "age": 15},
]
adults = list(filter(lambda u: u["age"] >= 18, users))
print([u["name"] for u in adults])  # ['Alice', 'Charlie']

5.3 reduce()

对序列元素进行累积运算,最终得到一个值。

python
from functools import reduce

# 累加
nums = [1, 2, 3, 4, 5]
total = reduce(lambda a, b: a + b, nums)
print(total)  # 15  过程:((((1+2)+3)+4)+5)

# 累乘
product = reduce(lambda a, b: a * b, nums)
print(product)  # 120

# 带初始值
total = reduce(lambda a, b: a + b, nums, 100)
print(total)  # 115  (初始值 100 + 15)

# 找最大值
maximum = reduce(lambda a, b: a if a > b else b, nums)
print(maximum)  # 5

# 实用示例:展平嵌套列表
nested = [[1, 2], [3, 4], [5, 6]]
flat = reduce(lambda a, b: a + b, nested)
print(flat)  # [1, 2, 3, 4, 5, 6]

5.4 sorted() 的 key 参数

python
# 按字符串长度排序
words = ["python", "is", "a", "great", "language"]
print(sorted(words, key=len))
# ['a', 'is', 'great', 'python', 'language']

# 忽略大小写排序
words = ["Banana", "apple", "Cherry"]
print(sorted(words, key=str.lower))
# ['apple', 'Banana', 'Cherry']

# 复杂排序:使用 operator 模块
from operator import itemgetter, attrgetter

students = [("Alice", 90), ("Bob", 85), ("Charlie", 92)]
print(sorted(students, key=itemgetter(1), reverse=True))
# [('Charlie', 92), ('Alice', 90), ('Bob', 85)]

# 多级排序
data = [("A", 2), ("B", 1), ("A", 1), ("B", 2)]
print(sorted(data, key=itemgetter(0, 1)))
# [('A', 1), ('A', 2), ('B', 1), ('B', 2)]

5.5 函数作为参数和返回值

python
# 函数作为参数
def apply(func, value):
    return func(value)

print(apply(abs, -10))         # 10
print(apply(str.upper, "hi"))  # HI
print(apply(lambda x: x * 2, 5))  # 10

# 函数作为返回值
def make_power(n):
    def power(x):
        return x ** n
    return power

square = make_power(2)
cube = make_power(3)
print(square(5))  # 25
print(cube(5))    # 125

6. 递归函数

函数调用自身的编程技巧。需要有终止条件,否则会无限递归。

python
# 阶乘:n! = n × (n-1)!
def factorial(n):
    if n <= 1:       # 终止条件
        return 1
    return n * factorial(n - 1)

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

# 斐波那契数列:F(n) = F(n-1) + F(n-2)
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

print([fib(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# ⚠️ 上面的递归效率很低(大量重复计算)
# ✅ 使用缓存优化
from functools import lru_cache

@lru_cache(maxsize=None)
def fib_fast(n):
    if n <= 1:
        return n
    return fib_fast(n - 1) + fib_fast(n - 2)

print(fib_fast(50))   # 12586269025  (瞬间完成)
print(fib_fast(100))  # 354224848179261915075

# Python 默认递归深度限制为 1000
import sys
print(sys.getrecursionlimit())   # 1000
# sys.setrecursionlimit(2000)    # 可以修改,但不建议设太大

# 实用示例:递归遍历嵌套结构
def flatten(lst):
    result = []
    for item in lst:
        if isinstance(item, list):
            result.extend(flatten(item))  # 递归展平
        else:
            result.append(item)
    return result

nested = [1, [2, [3, 4], 5], [6, 7]]
print(flatten(nested))  # [1, 2, 3, 4, 5, 6, 7]

7. 函数注解与类型提示

类型提示不会影响运行,但能提高代码可读性,并配合工具做静态检查。

python
# 基本类型注解
def add(a: int, b: int) -> int:
    return a + b

print(add(3, 5))      # 8
print(add("a", "b"))  # ab  (类型注解不强制,运行时不报错)

# 常见类型注解
def greet(name: str) -> str:
    return f"Hello, {name}"

def is_even(n: int) -> bool:
    return n % 2 == 0

def process(data: list) -> None:
    print(data)

# 使用 typing 模块提供更精确的类型
from typing import Optional, Union

# Optional 表示可以是指定类型或 None
def find_user(user_id: int) -> Optional[str]:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

print(find_user(1))   # Alice
print(find_user(99))  # None

# Union 表示可以是多种类型之一
def double(x: Union[int, str]) -> Union[int, str]:
    if isinstance(x, int):
        return x * 2
    return x + x

print(double(5))       # 10
print(double("hi"))    # hihi

# Python 3.10+ 可以用 | 替代 Union
# def double(x: int | str) -> int | str:

# 容器类型注解
from typing import List, Dict, Tuple

def average(scores: List[float]) -> float:
    return sum(scores) / len(scores)

def get_info() -> Dict[str, int]:
    return {"age": 25, "score": 90}

def get_point() -> Tuple[int, int]:
    return (3, 4)

# Python 3.9+ 可以直接用内置类型
# def average(scores: list[float]) -> float:
# def get_info() -> dict[str, int]:

# 查看函数注解
print(add.__annotations__)
# {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

8. functools 常用工具

8.1 partial —— 偏函数

固定函数的部分参数,生成新函数。

python
from functools import partial

# 固定 base 参数
def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube = partial(power, exp=3)

print(square(5))   # 25
print(cube(5))     # 125

# 实用示例:简化 int 的进制转换
int2 = partial(int, base=2)    # 二进制转整数
int16 = partial(int, base=16)  # 十六进制转整数

print(int2("1010"))    # 10
print(int16("ff"))     # 255

# 简化 print
debug = partial(print, "[DEBUG]")
debug("程序启动")     # [DEBUG] 程序启动
debug("加载配置")     # [DEBUG] 加载配置

8.2 lru_cache —— 缓存装饰器

python
from functools import lru_cache

# 自动缓存函数结果,相同参数不会重复计算
@lru_cache(maxsize=128)
def expensive_calc(n):
    print(f"  计算 {n}...")  # 只有首次调用时打印
    return n ** 2

print(expensive_calc(4))  #   计算 4...  →  16
print(expensive_calc(4))  #   16  (直接返回缓存,不再打印"计算")
print(expensive_calc(5))  #   计算 5...  →  25

# 查看缓存信息
print(expensive_calc.cache_info())
# CacheInfo(hits=1, misses=2, maxsize=128, currsize=2)

# 清除缓存
expensive_calc.cache_clear()

# Python 3.9+ 可以用 @cache,等价于 @lru_cache(maxsize=None)
from functools import cache

@cache
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(100))  # 354224848179261915075

8.3 wraps —— 保留函数元信息

python
from functools import wraps

# 不使用 wraps 的问题
def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def say_hello():
    """这是 say_hello 的文档"""
    print("Hello!")

print(say_hello.__name__)  # wrapper  (函数名丢失了!)
print(say_hello.__doc__)   # None     (文档也丢失了!)

# ✅ 使用 wraps 保留原函数信息
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def say_hello():
    """这是 say_hello 的文档"""
    print("Hello!")

print(say_hello.__name__)  # say_hello  ✅
print(say_hello.__doc__)   # 这是 say_hello 的文档  ✅

9. 内置函数补充

9.1 eval() / exec()

python
# eval() —— 执行表达式并返回结果
print(eval("3 + 5"))         # 8
print(eval("'hello'.upper()"))  # HELLO

x = 10
print(eval("x * 2"))         # 20

# exec() —— 执行语句(无返回值)
exec("y = 100")
print(y)  # 100

exec("""
def dynamic_func(n):
    return n * 2
""")
print(dynamic_func(5))  # 10

# ⚠️ 安全警告:永远不要对用户输入使用 eval/exec
# eval(input("输入表达式:"))  # 极其危险!

9.2 zip() 进阶用法

python
# 基本用法
names = ["Alice", "Bob", "Charlie"]
scores = [90, 85, 92]
print(list(zip(names, scores)))
# [('Alice', 90), ('Bob', 85), ('Charlie', 92)]

# 构建字典
name_score = dict(zip(names, scores))
print(name_score)  # {'Alice': 90, 'Bob': 85, 'Charlie': 92}

# 解压(zip 的逆操作)
pairs = [("Alice", 90), ("Bob", 85), ("Charlie", 92)]
names, scores = zip(*pairs)
print(names)   # ('Alice', 'Bob', 'Charlie')
print(scores)  # (90, 85, 92)

# 矩阵转置
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [list(row) for row in zip(*matrix)]
print(transposed)  # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

# strict 模式(Python 3.10+),长度不等时报错
# list(zip([1, 2], [3, 4, 5], strict=True))  # ValueError

9.3 enumerate() 进阶

python
# 指定起始索引
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")
# 1. apple
# 2. banana
# 3. cherry

# 实用:查找元素的所有索引
nums = [1, 3, 5, 3, 7, 3, 9]
indices = [i for i, x in enumerate(nums) if x == 3]
print(indices)  # [1, 3, 5]

10. 综合示例

示例 1:简易计算器

python
def calculator(a, b, op="+"):
    ops = {
        "+": lambda x, y: x + y,
        "-": lambda x, y: x - y,
        "*": lambda x, y: x * y,
        "/": lambda x, y: x / y if y != 0 else "除数不能为零",
    }
    func = ops.get(op)
    if func is None:
        return f"不支持的运算符:{op}"
    return func(a, b)

print(calculator(10, 3, "+"))   # 13
print(calculator(10, 3, "-"))   # 7
print(calculator(10, 3, "*"))   # 30
print(calculator(10, 3, "/"))   # 3.3333333333333335
print(calculator(10, 0, "/"))   # 除数不能为零
print(calculator(10, 3, "%"))   # 不支持的运算符:%

示例 2:通用重试函数

python
import random
from functools import wraps

def retry(max_attempts=3):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"  第 {attempt} 次尝试失败:{e}")
                    if attempt == max_attempts:
                        raise
        return wrapper
    return decorator

@retry(max_attempts=5)
def unstable_task():
    """模拟不稳定的操作"""
    if random.random() < 0.7:
        raise ConnectionError("连接失败")
    return "成功!"

try:
    result = unstable_task()
    print(result)
except ConnectionError:
    print("多次重试后仍然失败")

示例 3:管道式数据处理

python
def pipeline(data, *functions):
    """将数据依次通过多个函数处理"""
    result = data
    for func in functions:
        result = func(result)
    return result

# 定义处理步骤
remove_spaces = lambda s: s.strip()
to_lower = lambda s: s.lower()
replace_spaces = lambda s: s.replace(" ", "_")
add_prefix = lambda s: f"user_{s}"

# 组合使用
username = pipeline(
    "  Hello World  ",
    remove_spaces,
    to_lower,
    replace_spaces,
    add_prefix,
)
print(username)  # user_hello_world

11. 总结

概念说明示例
def定义函数def add(a, b): return a + b
return返回值return x, y (返回元组)
*args可变位置参数def f(*args) → 元组
**kwargs可变关键字参数def f(**kwargs) → 字典
lambda匿名函数lambda x: x * 2
global声明全局变量global count
nonlocal声明外层变量nonlocal n
闭包内层函数 + 外层变量函数工厂模式
类型提示提高可读性def add(a: int) -> int
lru_cache缓存函数结果@lru_cache(maxsize=128)
partial固定部分参数partial(int, base=2)

函数设计原则

python
# 1. 单一职责:一个函数只做一件事
# ✅ 好
def calculate_total(prices):
    return sum(prices)

def format_price(amount):
    return f{amount:.2f}"

# ❌ 不好:既计算又格式化
def calculate_and_format(prices):
    total = sum(prices)
    return f{total:.2f}"

# 2. 合理使用默认参数,避免可变默认值
# 3. 函数名用小写加下划线(snake_case)
# 4. 优先使用返回值而非修改全局变量
# 5. 文档字符串说明参数和返回值的含义

Released under the MIT License.