02 - 函数与模块¶
学习时间: 45-60分钟 重要性: ⭐⭐⭐⭐⭐ 组织代码的基本单位
🎯 学习目标¶
- 理解函数的定义和调用
- 掌握参数传递的各种方式
- 理解作用域和命名空间
- 学会组织和使用模块
🔧 函数基础¶
定义和调用¶
Python
# 基本语法
def greet():
"""函数文档字符串(docstring)"""
print("Hello!")
greet() # 调用函数
# 带参数的函数
def greet_person(name):
print(f"Hello, {name}!")
greet_person("张三") # "Hello, 张三!"
# 返回值
def add(a, b):
return a + b
result = add(5, 3) # result = 8
# 没有return语句,默认返回None
def no_return():
print("这个函数不返回值")
result = no_return() # result = None
参数类型(重要!)¶
Python
# 1. 位置参数
def describe_person(name, age):
print(f"{name} is {age} years old")
describe_person("张三", 25) # 按位置传递
# 2. 关键字参数
describe_person(name="李四", age=30)
describe_person(age=30, name="李四") # 顺序可以改变
# 3. 默认参数
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("张三") # "Hello, 张三!"
greet("张三", greeting="你好") # "你好, 张三!"
# ⚠️ 警告:默认参数只计算一次
def append_to_list(item, lst=[]): # 危险!
lst.append(item)
return lst
print(append_to_list(1)) # [1]
print(append_to_list(2)) # [1, 2] - lst保留了之前的值!
# ✅ 正确做法
def append_to_list(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
# 4. 可变位置参数 (*args)
def sum_all(*numbers):
return sum(numbers)
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4)) # 10
def print_info(*args):
for arg in args:
print(arg)
print_info("张三", 25, "北京")
# 5. 可变关键字参数 (**kwargs)
def create_profile(**info):
for key, value in info.items():
print(f"{key}: {value}")
create_profile(name="张三", age=25, city="北京")
# 组合使用(参数顺序很重要)
def complex_function(a, b, *args, c=10, **kwargs):
print(f"a: {a}, b: {b}")
print(f"args: {args}")
print(f"c: {c}")
print(f"kwargs: {kwargs}")
complex_function(1, 2, 3, 4, c=20, name="张三")
# a: 1, b: 2
# args: (3, 4)
# c: 20
# kwargs: {'name': '张三'}
返回多个值¶
Python
# 返回元组(实际上是返回一个对象)
def get_user_info():
name = "张三"
age = 25
city = "北京"
return name, age, city # 等价于 return (name, age, city)
# 解包接收
name, age, city = get_user_info()
# 或者作为元组接收
info = get_user_info()
print(info) # ("张三", 25, "北京")
# 返回字典(更清晰)
def get_user_info_dict():
return {
"name": "张三",
"age": 25,
"city": "北京"
}
info = get_user_info_dict()
print(info["name"]) # "张三"
📦 作用域与命名空间¶
LEGB规则¶
Python
# L - Local(局部)
# E - Enclosing(闭包)
# G - Global(全局)
# B - Built-in(内置)
x = "global" # 全局变量
def outer():
x = "enclosing" # 闭包变量
def inner():
x = "local" # 局部变量
print(x) # 输出: local
inner()
print(x) # 输出: enclosing
outer()
print(x) # 输出: global
global 和 nonlocal¶
Python
# global - 修改全局变量
count = 0
def increment():
global count # 声明使用全局变量
count += 1
increment()
print(count) # 1
# nonlocal - 修改闭包变量
def outer():
count = 0
def inner():
nonlocal count
count += 1
inner()
return count
print(outer()) # 1
变量查找示例¶
Python
x = 10 # 全局
def func():
x = 20 # 局部
print(x) # 20 (优先使用局部)
func()
print(x) # 10 (全局没有被修改)
def func2():
print(x) # 10 (没有局部变量,使用全局)
func2()
def func3():
global x
x = 30 # 修改全局变量
func3()
print(x) # 30
🗂️ 模块与包¶
导入模块¶
Python
# 导入整个模块
import math
print(math.sqrt(16)) # 4.0
# 导入并重命名
import numpy as np
import pandas as pd
# 从模块导入特定函数
from math import sqrt, pi
print(sqrt(16)) # 4.0 (直接使用,不需要math.)
print(pi) # 3.14159...
# 导入所有(不推荐)
from math import *
# 问题:污染命名空间,可能覆盖现有变量
# 推荐做法:明确导入需要的
from math import sqrt, sin, cos
创建自己的模块¶
Python
# my_utils.py
def greet(name):
return f"Hello, {name}!"
def calculate_sum(numbers):
return sum(numbers)
PI = 3.14159 # 示例常量;实际项目中建议使用 math.pi 获取更精确的值
# 另一个文件中使用
# main.py
import my_utils
print(my_utils.greet("张三"))
print(my_utils.calculate_sum([1, 2, 3]))
print(my_utils.PI)
# 或者
from my_utils import greet, calculate_sum
print(greet("张三"))
包(Package)¶
Python
# __init__.py
from .utils import helper_function
from .models import Model
# 使用
import my_package
my_package.helper_function()
from my_package import utils, models
utils.helper_function()
模块搜索路径¶
Python
import sys
print(sys.path)
# Python按以下顺序查找模块:
# 1. 当前目录
# 2. PYTHONPATH环境变量
# 3. 标准库路径
# 4. site-packages目录(第三方库)
🎯 函数最佳实践¶
1. 单一职责原则¶
Python
# ❌ 不好:函数做太多事情
def process_data(data):
# 读取数据
# 清洗数据
# 训练模型
# 保存结果
pass
# ✅ 好:每个函数只做一件事
def read_data(filepath):
pass
def clean_data(data):
pass
def train_model(data):
pass
def save_results(results, filepath):
pass
2. 使用类型注解(Python 3.9+ 推荐内置泛型)¶
Python
from typing import Any
def calculate_sum(numbers: list[int]) -> int:
"""计算数字列表的和"""
return sum(numbers)
def find_user(user_id: int) -> dict | None:
"""查找用户,如果不存在返回None"""
pass
def process_data(
data: list[dict[str, Any]],
limit: int = 10
) -> dict[str, int]:
"""处理数据并返回统计"""
pass
3. 编写清晰的文档字符串¶
Python
def train_model(
data: np.ndarray,
epochs: int = 100,
learning_rate: float = 0.001
) -> Model:
"""
训练机器学习模型
参数:
data: 训练数据,形状为(n_samples, n_features)
epochs: 训练轮数,默认为100
learning_rate: 学习率,默认为0.001
返回:
训练好的模型对象
异常:
ValueError: 如果数据为空或形状不正确
示例:
>>> model = train_model(X_train, epochs=50)
>>> predictions = model.predict(X_test)
"""
pass
4. 使用纯函数(无副作用)¶
Python
# ❌ 有副作用:修改输入参数
def add_tax_bad(prices):
prices.append(prices[-1] * 0.1) # 修改了原列表
return prices
# ✅ 纯函数:不修改输入
def add_tax_good(prices):
new_prices = prices.copy() # 创建副本
new_prices.append(prices[-1] * 0.1)
return new_prices
# 或者
def add_tax_better(prices):
return prices + [prices[-1] * 0.1]
💡 实用技巧¶
高阶函数¶
Python
# 函数作为参数
def apply_operation(x, operation):
return operation(x)
result = apply_operation(5, lambda x: x ** 2) # lambda匿名函数:lambda 参数: 表达式
# 常用高阶函数
numbers = [1, 2, 3, 4, 5]
# map: 对每个元素应用函数
squared = list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
# filter: 过滤元素
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]
# 列表推导式通常更清晰
squared = [x ** 2 for x in numbers]
evens = [x for x in numbers if x % 2 == 0]
装饰器基础(下一章详解)¶
Python
# 简单的计时装饰器
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.2f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "Done"
slow_function() # 会打印执行时间
函数缓存¶
Python
from functools import lru_cache
@lru_cache(maxsize=128) # 缓存最近128次调用
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 第一次调用慢
print(fibonacci(100))
# 第二次调用快(从缓存读取)
print(fibonacci(100))
📝 练习¶
练习1: 数据处理函数¶
Python
def filter_and_transform(
numbers: list[int],
min_value: int = 0,
max_value: int = 100
) -> list[int]:
"""
1. 筛选出min_value到max_value之间的数字
2. 将筛选出的数字乘以2
3. 返回结果列表
"""
# TODO: 实现这个函数
pass
# 测试
print(filter_and_transform([1, 50, 150, 75, -10])) # 应该输出 [2, 100, 150]
练习2: 统计函数¶
Python
def analyze_text(text: str) -> dict:
"""
分析文本并返回统计信息:
- 字符数
- 单词数
- 句子数(以.!?结尾)
"""
# TODO: 实现这个函数
pass
# 测试
text = "Hello world. How are you? I'm fine!"
print(analyze_text(text))
# 应该输出类似: {'chars': 37, 'words': 8, 'sentences': 3}
练习3: 创建模块¶
Python
# 创建一个文件 text_utils.py,包含以下函数:
# - count_words(text): 统计单词数
# - reverse_text(text): 反转文本
# - remove_punctuation(text): 去除标点符号
# 然后在另一个文件中导入使用
🎯 自我检查¶
完成这个主题后,你应该:
- 能定义和使用各种类型的参数
- 理解LEGB作用域规则
- 知道如何组织模块和包
- 能写出类型注解和文档字符串
- 理解纯函数的概念
- 不查资料完成上面的练习
📚 延伸阅读¶
下一步: 03 - 面向对象基础