📚 本教程是Python学习的第二天,专注于Python的高级特性,适合已掌握基础语法的学习者
6:函数作为一等公民
什么是"一等公民"?
在Python中,函数是"一等公民"(First-class citizen),这意味着函数可以:
-
被赋值给变量
-
作为参数传递给其他函数
-
作为函数的返回值
-
存储在数据结构中
函数赋值给变量
def greet(name):
"""简单的问候函数"""
return f"你好,{name}!"
# 将函数赋值给变量
my_function = greet
print(my_function("小明")) # 输出:你好,小明!
# 函数名实际上就是一个变量,指向函数对象
print(type(greet)) # <class 'function'>
print(greet.__name__) # greet
函数作为参数传递
def add(a, b):
"""加法函数"""
return a + b
def multiply(a, b):
"""乘法函数"""
return a * b
def calculate(func, x, y):
"""
高阶函数:接受一个函数作为参数
func: 要执行的运算函数
x, y: 运算的两个数
"""
result = func(x, y)
print(f"使用 {func.__name__} 计算 {x} 和 {y} 的结果是: {result}")
return result
# 将函数作为参数传递
calculate(add, 5, 3) # 输出:使用 add 计算 5 和 3 的结果是: 8
calculate(multiply, 5, 3) # 输出:使用 multiply 计算 5 和 3 的结果是: 15
函数作为返回值
def create_multiplier(n):
"""
工厂函数:返回一个乘法函数
n: 乘数
返回一个新的乘法函数
"""
def multiplier(x):
"""内部函数:实际的乘法操作"""
return x * n
return multiplier # 返回函数对象
# 创建不同的乘法器
double = create_multiplier(2) # 创建一个乘以2的函数
triple = create_multiplier(3) # 创建一个乘以3的函数
print(double(5)) # 输出:10 (5 * 2)
print(triple(5)) # 输出:15 (5 * 3)
Lambda函数(匿名函数)
Lambda函数是一种创建小型匿名函数的方式:
# 传统函数定义
def square(x):
return x ** 2
# 使用lambda的等价写法
square_lambda = lambda x: x ** 2
print(square(5)) # 输出:25
print(square_lambda(5)) # 输出:25
# lambda函数的常见用法
numbers = [1, 2, 3, 4, 5]
# 1. 配合map()使用
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# 2. 配合filter()使用
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
# 3. 配合sorted()使用
students = [('小明', 85), ('小红', 92), ('小刚', 78)]
# 按成绩排序
sorted_students = sorted(students, key=lambda student: student[1])
print(sorted_students) # [('小刚', 78), ('小明', 85), ('小红', 92)]
实现自定义的map函数
def my_map(func, iterable):
"""
自定义实现map函数
func: 要应用的函数
iterable: 可迭代对象
返回: 应用函数后的结果列表
"""
result = []
for item in iterable:
result.append(func(item)) # 对每个元素应用函数
return result
# 测试自定义map函数
numbers = [1, 2, 3, 4, 5]
# 使用内置函数
def cube(x):
return x ** 3
cubed_numbers = my_map(cube, numbers)
print(cubed_numbers) # [1, 8, 27, 64, 125]
# 使用lambda函数
doubled_numbers = my_map(lambda x: x * 2, numbers)
print(doubled_numbers) # [2, 4, 6, 8, 10]
函数存储在数据结构中
# 将函数存储在字典中
operations = {
'add': lambda x, y: x + y,
'subtract': lambda x, y: x - y,
'multiply': lambda x, y: x * y,
'divide': lambda x, y: x / y if y != 0else"除零错误"
}
# 使用字典中的函数
print(operations['add'](10, 5)) # 输出:15
print(operations['multiply'](3, 4)) # 输出:12
# 将函数存储在列表中
validators = [
lambda x: x > 0, # 检查是否为正数
lambda x: len(str(x)) < 5, # 检查数字位数是否小于5
lambda x: x % 2 == 0 # 检查是否为偶数
]
def validate_number(num, validators):
"""
使用验证器列表验证数字
num: 要验证的数字
validators: 验证函数列表
"""
for i, validator in enumerate(validators):
ifnot validator(num):
returnf"验证失败:第{i+1}个条件不满足"
return"验证通过"
print(validate_number(12, validators)) # 输出:验证通过
print(validate_number(-5, validators)) # 输出:验证失败:第1个条件不满足
实际应用:事件处理系统
class EventHandler:
"""简单的事件处理系统"""
def __init__(self):
self.handlers = {} # 存储事件和对应的处理函数
def register(self, event_name, handler_func):
"""
注册事件处理函数
event_name: 事件名称
handler_func: 处理函数
"""
if event_name notin self.handlers:
self.handlers[event_name] = []
self.handlers[event_name].append(handler_func)
def trigger(self, event_name, *args, **kwargs):
"""
触发事件,执行所有注册的处理函数
event_name: 事件名称
*args, **kwargs: 传递给处理函数的参数
"""
if event_name in self.handlers:
for handler in self.handlers[event_name]:
handler(*args, **kwargs)
# 创建事件处理器
event_handler = EventHandler()
# 定义事件处理函数
def on_user_login(username):
print(f"用户 {username} 已登录")
def log_user_activity(username):
print(f"记录用户 {username} 的活动日志")
def send_welcome_email(username):
print(f"向用户 {username} 发送欢迎邮件")
# 注册事件处理函数
event_handler.register('user_login', on_user_login)
event_handler.register('user_login', log_user_activity)
event_handler.register('user_login', send_welcome_email)
# 触发事件
event_handler.trigger('user_login', '小明')
# 输出:
# 用户 小明 已登录
# 记录用户 小明 的活动日志
# 向用户 小明 发送欢迎邮件
7:闭包与装饰器
什么是闭包?
闭包(Closure)是指内部函数引用了外部函数的变量,即使外部函数已经执行完毕,内部函数仍然可以访问这些变量。
def outer_function(x):
"""外部函数"""
message = f"外部变量 x = {x}"
def inner_function():
"""内部函数 - 这就是闭包"""
print(message) # 访问外部函数的变量
print(f"内部函数访问到:x = {x}")
return inner_function # 返回内部函数
# 创建闭包
closure = outer_function(42)
closure() # 调用闭包
# 输出:
# 外部变量 x = 42
# 内部函数访问到:x = 42
# 即使outer_function已经执行完毕,closure仍然记住了x的值
闭包的实际应用:计数器
def create_counter(initial_value=0):
"""
创建一个计数器闭包
initial_value: 初始计数值
返回: 计数器函数
"""
count = initial_value # 这个变量会被闭包"捕获"
def counter():
"""内部计数器函数"""
nonlocal count # 声明要修改外部函数的变量
count += 1
return count
return counter
# 创建两个独立的计数器
counter1 = create_counter()
counter2 = create_counter(100)
# 使用计数器
print(counter1()) # 输出:1
print(counter1()) # 输出:2
print(counter2()) # 输出:101
print(counter1()) # 输出:3
print(counter2()) # 输出:102
# 每个计数器都有自己独立的count变量
闭包实现银行账户
def create_account(initial_balance):
"""
创建一个银行账户闭包
initial_balance: 初始余额
返回: 包含操作方法的字典
"""
balance = initial_balance # 余额变量被闭包捕获
def deposit(amount):
"""存款"""
nonlocal balance
if amount > 0:
balance += amount
returnf"存款 {amount} 元成功,当前余额:{balance} 元"
return"存款金额必须大于0"
def withdraw(amount):
"""取款"""
nonlocal balance
if amount > 0and amount <= balance:
balance -= amount
returnf"取款 {amount} 元成功,当前余额:{balance} 元"
elif amount > balance:
return"余额不足"
return"取款金额必须大于0"
def get_balance():
"""查询余额"""
returnf"当前余额:{balance} 元"
# 返回操作方法
return {
'deposit': deposit,
'withdraw': withdraw,
'balance': get_balance
}
# 创建账户
account = create_account(1000)
# 进行操作
print(account['balance']()) # 输出:当前余额:1000 元
print(account['deposit'](500)) # 输出:存款 500 元成功,当前余额:1500 元
print(account['withdraw'](200)) # 输出:取款 200 元成功,当前余额:1300 元
print(account['withdraw'](2000)) # 输出:余额不足
装饰器基础
装饰器是Python中一种特殊的语法,用于修改或增强函数的功能,而不需要修改函数本身的代码。
def my_decorator(func):
"""
简单的装饰器函数
func: 被装饰的函数
返回: 包装后的函数
"""
def wrapper():
"""包装函数"""
print("函数执行前的操作")
result = func() # 调用原始函数
print("函数执行后的操作")
return result
return wrapper
# 使用装饰器的两种方式
# 方式1:手动应用装饰器
def say_hello():
print("Hello, World!")
decorated_hello = my_decorator(say_hello)
decorated_hello()
# 输出:
# 函数执行前的操作
# Hello, World!
# 函数执行后的操作
# 方式2:使用@语法糖(推荐)
@my_decorator
def say_goodbye():
print("Goodbye, World!")
say_goodbye() # 自动应用了装饰器
# 输出:
# 函数执行前的操作
# Goodbye, World!
# 函数执行后的操作
计时装饰器
import time
from functools import wraps
def timer(func):
"""
计时装饰器:测量函数执行时间
func: 被装饰的函数
"""
@wraps(func) # 保持原函数的元信息
def wrapper(*args, **kwargs):
"""包装函数,支持任意参数"""
start_time = time.time() # 记录开始时间
print(f"开始执行函数 {func.__name__}")
result = func(*args, **kwargs) # 执行原函数
end_time = time.time() # 记录结束时间
execution_time = end_time - start_time
print(f"函数 {func.__name__} 执行完毕,耗时:{execution_time:.4f} 秒")
return result
return wrapper
# 使用计时装饰器
@timer
def slow_function():
"""一个执行较慢的函数"""
print("正在执行耗时操作...")
time.sleep(2) # 模拟耗时操作
return"操作完成"
@timer
def calculate_sum(n):
"""计算1到n的和"""
total = sum(range(1, n + 1))
print(f"1到{n}的和是:{total}")
return total
# 测试装饰器
result1 = slow_function()
print(f"返回值:{result1}\n")
result2 = calculate_sum(1000000)
print(f"返回值:{result2}")
日志装饰器
import datetime
from functools import wraps
def log_function_call(log_file=None):
"""
日志装饰器:记录函数调用信息
log_file: 日志文件路径,如果为None则打印到控制台
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 获取当前时间
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 构造日志信息
args_str = ', '.join(map(str, args))
kwargs_str = ', '.join(f"{k}={v}"for k, v in kwargs.items())
all_args = ', '.join(filter(None, [args_str, kwargs_str]))
log_message = f"[{timestamp}] 调用函数 {func.__name__}({all_args})"
try:
# 执行函数
result = func(*args, **kwargs)
log_message += f" -> 返回值: {result}"
# 写入日志
if log_file:
with open(log_file, 'a', encoding='utf-8') as f:
f.write(log_message + '\n')
else:
print(log_message)
return result
except Exception as e:
# 记录异常
error_message = f"{log_message} -> 发生异常: {str(e)}"
if log_file:
with open(log_file, 'a', encoding='utf-8') as f:
f.write(error_message + '\n')
else:
print(error_message)
raise# 重新抛出异常
return wrapper
return decorator
# 使用日志装饰器
@log_function_call() # 输出到控制台
def add_numbers(a, b):
"""加法函数"""
return a + b
@log_function_call('function_calls.log') # 输出到文件
def divide_numbers(a, b):
"""除法函数"""
return a / b
# 测试日志装饰器
result1 = add_numbers(10, 5)
result2 = divide_numbers(10, 2)
try:
result3 = divide_numbers(10, 0) # 这会产生异常
except ZeroDivisionError:
print("捕获到除零错误")
带参数的装饰器
def repeat(times):
"""
重复执行装饰器
times: 重复执行次数
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
results = []
for i in range(times):
print(f"第 {i+1} 次执行:")
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(3) # 重复执行3次
def greet(name):
print(f"你好,{name}!")
returnf"问候了 {name}"
# 测试重复装饰器
results = greet("小明")
print(f"所有执行结果:{results}")
装饰器的实际应用:权限检查
from functools import wraps
# 模拟用户权限系统
current_user = {'name': '小明', 'role': 'admin'}
def require_permission(required_role):
"""
权限检查装饰器
required_role: 需要的角色权限
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 检查当前用户权限
if current_user.get('role') == required_role:
print(f"权限验证通过,用户 {current_user['name']} 有 {required_role} 权限")
return func(*args, **kwargs)
else:
print(f"权限不足!需要 {required_role} 权限,当前用户权限:{current_user.get('role', '无')}")
returnNone
return wrapper
return decorator
@require_permission('admin')
def delete_user(username):
"""删除用户 - 需要管理员权限"""
print(f"用户 {username} 已被删除")
returnf"删除操作成功"
@require_permission('user')
def view_profile():
"""查看个人资料 - 需要普通用户权限"""
print("显示个人资料")
return"个人资料数据"
# 测试权限装饰器
delete_user('测试用户') # 有权限,可以执行
view_profile() # 有权限,可以执行
# 切换到普通用户
current_user = {'name': '小红', 'role': 'user'}
delete_user('另一个用户') # 权限不足,无法执行
view_profile() # 有权限,可以执行
类装饰器
class CallCounter:
"""调用计数装饰器类"""
def __init__(self, func):
"""
初始化装饰器
func: 被装饰的函数
"""
self.func = func
self.count = 0
wraps(func)(self) # 保持原函数信息
def __call__(self, *args, **kwargs):
"""让对象可以像函数一样被调用"""
self.count += 1
print(f"函数 {self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)
def get_count(self):
"""获取调用次数"""
return self.count
@CallCounter
def say_hello(name):
returnf"Hello, {name}!"
# 测试类装饰器
print(say_hello("Alice"))
print(say_hello("Bob"))
print(say_hello("Charlie"))
print(f"总调用次数:{say_hello.get_count()}")
8:生成器与迭代器
什么是生成器?
生成器是Python中的一种特殊函数,使用yield关键字而不是return。生成器可以暂停执行并保存状态,下次调用时从暂停的地方继续执行。
def simple_generator():
"""简单的生成器函数"""
print("生成器开始执行")
yield1# 第一次暂停,返回1
print("生成器继续执行")
yield2# 第二次暂停,返回2
print("生成器即将结束")
yield3# 第三次暂停,返回3
print("生成器执行完毕")
# 创建生成器对象
gen = simple_generator()
print(type(gen)) # <class 'generator'>
# 逐个获取值
print(next(gen)) # 输出:生成器开始执行 \n 1
print(next(gen)) # 输出:生成器继续执行 \n 2
print(next(gen)) # 输出:生成器即将结束 \n 3
# 如果继续调用next()会抛出StopIteration异常
try:
print(next(gen))
except StopIteration:
print("生成器已经结束")
生成器与普通函数的对比
# 普通函数:一次性返回所有结果
def create_numbers_list(n):
"""返回包含1到n所有数字的列表"""
result = []
for i in range(1, n + 1):
result.append(i ** 2) # 计算平方
return result
# 生成器函数:逐个产生结果
def create_numbers_generator(n):
"""生成1到n所有数字的平方"""
for i in range(1, n + 1):
print(f"正在计算 {i} 的平方")
yield i ** 2# 每次yield一个值
# 比较内存使用
import sys
# 普通列表(占用大量内存)
big_list = create_numbers_list(1000)
print(f"列表大小:{sys.getsizeof(big_list)} 字节")
# 生成器(只占用很少内存)
big_generator = create_numbers_generator(1000)
print(f"生成器大小:{sys.getsizeof(big_generator)} 字节")
# 使用生成器
print("前5个平方数:")
for i, square in enumerate(big_generator):
if i >= 5:
break
print(square)
斐波那契数列生成器
def fibonacci_generator(max_count=None):
"""
斐波那契数列生成器
max_count: 最大生成数量,None表示无限生成
"""
a, b = 0, 1# 初始化前两个数
count = 0
while max_count isNoneor count < max_count:
yield a # 返回当前数字
a, b = b, a + b # 计算下一个数字
count += 1
# 生成前10个斐波那契数
print("前10个斐波那契数:")
fib_gen = fibonacci_generator(10)
for num in fib_gen:
print(num, end=" ")
print()
# 生成斐波那契数直到超过100
print("小于100的斐波那契数:")
fib_gen = fibonacci_generator()
for num in fib_gen:
if num > 100:
break
print(num, end=" ")
print()
文件读取生成器
def read_file_lines(filename):
"""
逐行读取文件的生成器
filename: 文件名
优点:不会一次性将整个文件加载到内存中
"""
try:
with open(filename, 'r', encoding='utf-8') as file:
line_number = 1
for line in file:
# 去除行尾的换行符
clean_line = line.rstrip('\n')
yield line_number, clean_line
line_number += 1
except FileNotFoundError:
print(f"文件 {filename} 不存在")
return
# 创建测试文件
test_content = """第一行内容
第二行内容
第三行内容
这是第四行
最后一行"""
with open('test.txt', 'w', encoding='utf-8') as f:
f.write(test_content)
# 使用生成器读取文件
print("使用生成器逐行读取文件:")
for line_num, content in read_file_lines('test.txt'):
print(f"第{line_num}行: {content}")
处理大文件的生成器
def process_large_file(filename, chunk_size=1024):
"""
处理大文件的生成器,分块读取
filename: 文件名
chunk_size: 每次读取的字节数
"""
try:
with open(filename, 'r', encoding='utf-8') as file:
whileTrue:
chunk = file.read(chunk_size) # 读取指定大小的块
ifnot chunk: # 文件读取完毕
break
# 处理数据块(这里只是简单统计字符)
char_count = len(chunk)
word_count = len(chunk.split())
yield {
'chunk': chunk,
'char_count': char_count,
'word_count': word_count
}
except FileNotFoundError:
print(f"文件 {filename} 不存在")
return
# 模拟处理大文件
def analyze_large_file(filename):
"""分析大文件的内容"""
total_chars = 0
total_words = 0
chunk_count = 0
print(f"开始分析文件 {filename}...")
for chunk_info in process_large_file(filename, 50): # 每次读取50字符
chunk_count += 1
total_chars += chunk_info['char_count']
total_words += chunk_info['word_count']
print(f"处理第 {chunk_count} 块,字符数: {chunk_info['char_count']}, "
f"单词数: {chunk_info['word_count']}")
print(f"分析完成!总字符数: {total_chars}, 总单词数: {total_words}")
# 创建一个较大的测试文件
large_content = "这是一个测试文件。" * 20 + "\n我们用它来演示生成器处理大文件的能力。" * 15
with open('large_test.txt', 'w', encoding='utf-8') as f:
f.write(large_content)
# 分析文件
analyze_large_file('large_test.txt')
生成器表达式
生成器表达式类似于列表推导式,但使用圆括号而不是方括号:
# 列表推导式(立即创建所有元素)
squares_list = [x**2for x in range(10)]
print(f"列表: {squares_list}")
# 生成器表达式(惰性计算)
squares_gen = (x**2for x in range(10))
print(f"生成器: {squares_gen}")
# 使用生成器表达式
print("生成器中的值:")
for square in squares_gen:
print(square, end=" ")
print()
# 生成器表达式在函数中的应用
def sum_of_squares(n):
"""计算1到n的平方和"""
return sum(x**2for x in range(1, n+1)) # 注意这里直接使用生成器表达式
print(f"1到10的平方和: {sum_of_squares(10)}")
# 过滤生成器
even_squares = (x**2for x in range(20) if x % 2 == 0)
print("偶数的平方:", list(even_squares))
理解迭代器协议
class NumberIterator:
"""自定义数字迭代器"""
def __init__(self, start, end):
"""
初始化迭代器
start: 起始数字
end: 结束数字
"""
self.current = start
self.end = end
def __iter__(self):
"""返回迭代器对象本身"""
return self
def __next__(self):
"""返回下一个值"""
if self.current < self.end:
current = self.current
self.current += 1
return current
else:
raise StopIteration # 迭代结束
# 使用自定义迭代器
print("使用自定义迭代器:")
num_iter = NumberIterator(1, 6)
for num in num_iter:
print(num, end=" ")
print()
# 手动使用迭代器
print("手动调用迭代器:")
num_iter2 = NumberIterator(10, 13)
iterator = iter(num_iter2) # 获取迭代器
try:
whileTrue:
print(next(iterator))
except StopIteration:
print("迭代完成")
创建可迭代的类
class EvenNumbers:
"""生成偶数的可迭代类"""
def __init__(self, max_num):
"""
初始化
max_num: 最大数字
"""
self.max_num = max_num
def __iter__(self):
"""返回一个新的迭代器"""
return EvenNumbersIterator(self.max_num)
class EvenNumbersIterator:
"""偶数迭代器"""
def __init__(self, max_num):
self.max_num = max_num
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current <= self.max_num:
current = self.current
self.current += 2
return current
else:
raise StopIteration
# 使用可迭代类
print("20以内的偶数:")
even_nums = EvenNumbers(20)
for num in even_nums:
print(num, end=" ")
print()
# 可以多次迭代
print("再次迭代:")
for num in even_nums:
if num > 10:
break
print(num, end=" ")
print()
生成器的实际应用:数据流处理
def data_source():
"""模拟数据源"""
import random
for i in range(100):
# 模拟传感器数据:温度、湿度、压力
temperature = random.uniform(20, 35)
humidity = random.uniform(30, 80)
pressure = random.uniform(1000, 1020)
yield {
'timestamp': i,
'temperature': temperature,
'humidity': humidity,
'pressure': pressure
}
def filter_high_temperature(data_stream, threshold=30):
"""过滤高温数据"""
for data in data_stream:
if data['temperature'] > threshold:
yield data
def calculate_heat_index(data_stream):
"""计算体感温度指数"""
for data in data_stream:
# 简化的体感温度计算
heat_index = data['temperature'] + (data['humidity'] / 10)
data['heat_index'] = heat_index
yield data
def format_output(data_stream):
"""格式化输出"""
for data in data_stream:
yield (f"时间: {data['timestamp']:3d}, "
f"温度: {data['temperature']:.1f}°C, "
f"湿度: {data['humidity']:.1f}%, "
f"体感温度: {data['heat_index']:.1f}°C")
# 构建数据处理管道
print("高温预警系统:")
data_pipeline = format_output(
calculate_heat_index(
filter_high_temperature(
data_source(),
threshold=32
)
)
)
# 处理前10条高温数据
for i, formatted_data in enumerate(data_pipeline):
if i >= 10:
break
print(formatted_data)
内存效率对比示例
import sys
import time
def memory_comparison():
"""比较列表和生成器的内存使用"""
# 使用列表(内存密集)
def create_large_list(n):
return [x * 2for x in range(n)]
# 使用生成器(内存友好)
def create_large_generator(n):
for x in range(n):
yield x * 2
n = 100000
# 测试列表
start_time = time.time()
large_list = create_large_list(n)
list_memory = sys.getsizeof(large_list)
list_time = time.time() - start_time
# 测试生成器
start_time = time.time()
large_gen = create_large_generator(n)
gen_memory = sys.getsizeof(large_gen)
gen_time = time.time() - start_time
# 打印结果
print(f"创建{n}个元素的对比:")
print(f"列表内存使用: {list_memory:,} 字节, 创建时间: {list_time:.6f} 秒")
print(f"生成器内存使用: {gen_memory:,} 字节, 创建时间: {gen_time:.6f} 秒")
print(f"内存节省: {(list_memory - gen_memory) / list_memory * 100:.1f}%")
# 测试访问速度
print("\n访问前1000个元素的时间对比:")
# 列表访问
start_time = time.time()
list_sum = sum(x for i, x in enumerate(large_list) if i < 1000)
list_access_time = time.time() - start_time
# 生成器访问
start_time = time.time()
gen_sum = sum(x for i, x in enumerate(create_large_generator(n)) if i < 1000)
gen_access_time = time.time() - start_time
print(f"列表访问时间: {list_access_time:.6f} 秒")
print(f"生成器访问时间: {gen_access_time:.6f} 秒")
memory_comparison()
9:文件与异常处理
with语句和上下文管理器
with语句是处理文件的最佳实践,它会自动处理文件的打开和关闭:
# 传统的文件处理方式(不推荐)
def old_way_read_file(filename):
"""传统方式读取文件"""
try:
file = open(filename, 'r', encoding='utf-8')
content = file.read()
file.close() # 必须手动关闭文件
return content
except Exception as e:
print(f"读取文件出错: {e}")
returnNone
# 推荐的方式:使用with语句
def recommended_way_read_file(filename):
"""推荐方式读取文件"""
try:
with open(filename, 'r', encoding='utf-8') as file:
content = file.read()
return content # 文件会自动关闭,即使发生异常
except Exception as e:
print(f"读取文件出错: {e}")
returnNone
# 创建测试文件
with open('sample.txt', 'w', encoding='utf-8') as f:
f.write("这是一个测试文件\n包含多行内容\n用于演示文件操作")
# 测试两种方式
content1 = old_way_read_file('sample.txt')
content2 = recommended_way_read_file('sample.txt')
print("传统方式读取的内容:")
print(content1)
print("\nwith语句读取的内容:")
print(content2)
文件操作的各种模式
def demonstrate_file_modes():
"""演示不同的文件打开模式"""
# 1. 写入模式 'w' - 覆盖写入
with open('demo.txt', 'w', encoding='utf-8') as f:
f.write("这是第一行\n")
f.write("这是第二行\n")
# 2. 追加模式 'a' - 在文件末尾追加
with open('demo.txt', 'a', encoding='utf-8') as f:
f.write("这是追加的第三行\n")
f.write("这是追加的第四行\n")
# 3. 读取模式 'r' - 只读
with open('demo.txt', 'r', encoding='utf-8') as f:
print("完整内容:")
print(f.read())
# 4. 读写模式 'r+' - 可读可写
with open('demo.txt', 'r+', encoding='utf-8') as f:
content = f.read() # 先读取
f.write("\n这是r+模式添加的内容") # 再写入
# 5. 二进制模式 'rb', 'wb'
# 写入二进制数据
data = b"这是二进制数据"
with open('binary_demo.bin', 'wb') as f:
f.write(data)
# 读取二进制数据
with open('binary_demo.bin', 'rb') as f:
binary_content = f.read()
print(f"二进制内容: {binary_content}")
print(f"解码后: {binary_content.decode('utf-8')}")
demonstrate_file_modes()
逐行处理大文件
def process_large_file_line_by_line(filename):
"""逐行处理大文件,内存友好"""
line_count = 0
word_count = 0
char_count = 0
try:
with open(filename, 'r', encoding='utf-8') as file:
for line in file: # 逐行读取,不会一次性加载整个文件
line_count += 1
words = line.strip().split()
word_count += len(words)
char_count += len(line)
# 处理每一行(这里只是简单统计)
if line_count % 1000 == 0: # 每1000行报告一次进度
print(f"已处理 {line_count} 行")
return {
'lines': line_count,
'words': word_count,
'characters': char_count
}
except FileNotFoundError:
print(f"文件 {filename} 不存在")
returnNone
except PermissionError:
print(f"没有权限访问文件 {filename}")
returnNone
# 创建一个较大的测试文件
def create_large_file(filename, lines=5000):
"""创建一个大文件用于测试"""
with open(filename, 'w', encoding='utf-8') as f:
for i in range(lines):
f.write(f"这是第 {i+1} 行,包含一些测试内容用于演示大文件处理。\n")
# 创建测试文件并处理
create_large_file('large_file.txt')
stats = process_large_file_line_by_line('large_file.txt')
if stats:
print(f"文件统计: {stats}")
异常处理基础
def basic_exception_handling():
"""基础异常处理示例"""
# 1. 基本的try-except结构
try:
number = int(input("请输入一个数字: "))
result = 10 / number
print(f"10 除以 {number} 等于 {result}")
except ValueError:
print("输入的不是有效数字!")
except ZeroDivisionError:
print("除数不能为零!")
# 2. 捕获多种异常
try:
numbers = [1, 2, 3]
index = int(input("请输入要访问的索引: "))
print(f"索引 {index} 的值是: {numbers[index]}")
except (ValueError, IndexError) as e:
print(f"发生错误: {e}")
# 3. 捕获所有异常
try:
# 一些可能出错的代码
risky_operation()
except Exception as e:
print(f"发生未知错误: {e}")
print(f"错误类型: {type(e).__name__}")
def risky_operation():
"""一个可能出错的函数"""
import random
if random.random() < 0.5:
raise ValueError("随机错误!")
return"操作成功"
# 注释掉用户输入部分,改为自动演示
def demo_exception_handling():
"""演示异常处理"""
# 演示ValueError
try:
number = int("abc") # 这会引发ValueError
except ValueError as e:
print(f"捕获到ValueError: {e}")
# 演示ZeroDivisionError
try:
result = 10 / 0# 这会引发ZeroDivisionError
except ZeroDivisionError as e:
print(f"捕获到ZeroDivisionError: {e}")
# 演示IndexError
try:
numbers = [1, 2, 3]
print(numbers[10]) # 这会引发IndexError
except IndexError as e:
print(f"捕获到IndexError: {e}")
demo_exception_handling()
完整的异常处理结构
def complete_exception_handling():
"""演示完整的try-except-else-finally结构"""
def divide_numbers(a, b):
"""除法函数,演示完整异常处理"""
print(f"尝试计算 {a} / {b}")
try:
# 可能出错的代码
result = a / b
except ZeroDivisionError:
print("错误:除数不能为零")
returnNone
except TypeError:
print("错误:参数类型不正确")
returnNone
else:
# 只有try块成功执行时才会运行
print(f"计算成功:{a} / {b} = {result}")
return result
finally:
# 无论是否发生异常都会执行
print("除法运算结束\n")
# 测试不同情况
divide_numbers(10, 2) # 正常情况
divide_numbers(10, 0) # 除零错误
divide_numbers("10", 2) # 类型错误
complete_exception_handling()
自定义异常类
class CustomError(Exception):
"""自定义异常基类"""
pass
class ValidationError(CustomError):
"""验证错误异常"""
def __init__(self, message, field_name=None):
super().__init__(message)
self.field_name = field_name
class BusinessLogicError(CustomError):
"""业务逻辑错误异常"""
def __init__(self, message, error_code=None):
super().__init__(message)
self.error_code = error_code
def validate_age(age):
"""年龄验证函数"""
ifnot isinstance(age, int):
raise ValidationError("年龄必须是整数", "age")
if age < 0:
raise ValidationError("年龄不能为负数", "age")
if age > 150:
raise ValidationError("年龄不能超过150岁", "age")
returnTrue
def process_user_registration(username, age):
"""用户注册处理函数"""
try:
# 验证用户名
ifnot username or len(username) < 3:
raise ValidationError("用户名至少需要3个字符", "username")
# 验证年龄
validate_age(age)
# 模拟业务逻辑检查
if username.lower() == "admin":
raise BusinessLogicError("用户名'admin'已被保留", "USER_RESERVED")
print(f"用户 {username}({age}岁)注册成功!")
returnTrue
except ValidationError as e:
print(f"验证失败 - {e.field_name}: {e}")
returnFalse
except BusinessLogicError as e:
print(f"业务逻辑错误 - {e.error_code}: {e}")
returnFalse
# 测试自定义异常
test_cases = [
("Alice", 25), # 正常情况
("", 30), # 用户名太短
("Bob", -5), # 年龄为负
("Charlie", "25"), # 年龄类型错误
("admin", 30), # 保留用户名
]
for username, age in test_cases:
print(f"测试: 用户名='{username}', 年龄={age}")
process_user_registration(username, age)
print()
文件操作的异常处理
import os
import shutil
from datetime import datetime
def safe_file_operations(source_file, destination_file):
"""安全的文件操作,包含完整的异常处理"""
try:
# 检查源文件是否存在
ifnot os.path.exists(source_file):
raise FileNotFoundError(f"源文件 {source_file} 不存在")
# 检查源文件是否为文件(而不是目录)
ifnot os.path.isfile(source_file):
raise ValueError(f"{source_file} 不是一个文件")
# 检查目标目录是否存在,不存在则创建
dest_dir = os.path.dirname(destination_file)
if dest_dir andnot os.path.exists(dest_dir):
os.makedirs(dest_dir)
print(f"创建目录: {dest_dir}")
# 备份现有文件
if os.path.exists(destination_file):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = f"{destination_file}.backup_{timestamp}"
shutil.copy2(destination_file, backup_file)
print(f"备份现有文件到: {backup_file}")
# 复制文件
with open(source_file, 'r', encoding='utf-8') as src:
with open(destination_file, 'w', encoding='utf-8') as dst:
content = src.read()
dst.write(content)
print(f"成功复制文件: {source_file} -> {destination_file}")
returnTrue
except FileNotFoundError as e:
print(f"文件不存在错误: {e}")
returnFalse
except PermissionError as e:
print(f"权限错误: {e}")
print("请检查文件权限或以管理员身份运行")
returnFalse
except OSError as e:
print(f"操作系统错误: {e}")
returnFalse
except UnicodeDecodeError as e:
print(f"文件编码错误: {e}")
print("尝试使用不同的编码格式")
returnFalse
except Exception as e:
print(f"未知错误: {type(e).__name__}: {e}")
returnFalse
finally:
print("文件操作结束")
# 创建测试文件和场景
def create_test_scenarios():
"""创建测试场景"""
# 创建源文件
with open('source.txt', 'w', encoding='utf-8') as f:
f.write("这是源文件的内容\n包含多行文本\n用于测试文件操作")
# 测试各种情况
print("=== 测试正常复制 ===")
safe_file_operations('source.txt', 'destination.txt')
print("\n=== 测试复制到子目录 ===")
safe_file_operations('source.txt', 'backup/destination.txt')
print("\n=== 测试源文件不存在 ===")
safe_file_operations('nonexistent.txt', 'destination2.txt')
print("\n=== 测试覆盖现有文件 ===")
safe_file_operations('source.txt', 'destination.txt')
create_test_scenarios()
记录异常信息
import logging
import traceback
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('error.log', encoding='utf-8'),
logging.StreamHandler()
]
)
def log_exceptions(func):
"""装饰器:记录函数异常"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
# 记录详细的异常信息
error_info = {
'function': func.__name__,
'args': args,
'kwargs': kwargs,
'error_type': type(e).__name__,
'error_message': str(e),
'traceback': traceback.format_exc()
}
logging.error(f"函数 {func.__name__} 发生异常:")
logging.error(f"错误类型: {error_info['error_type']}")
logging.error(f"错误信息: {error_info['error_message']}")
logging.error(f"完整追踪:\n{error_info['traceback']}")
# 可以选择重新抛出异常或返回默认值
raise
return wrapper
@log_exceptions
def risky_calculation(x, y, operation):
"""可能出错的计算函数"""
operations = {
'add': lambda a, b: a + b,
'subtract': lambda a, b: a - b,
'multiply': lambda a, b: a * b,
'divide': lambda a, b: a / b,
'power': lambda a, b: a ** b
}
if operation notin operations:
raise ValueError(f"不支持的操作: {operation}")
result = operations[operation](x, y)
logging.info(f"计算成功: {x} {operation} {y} = {result}")
return result
# 测试异常记录
test_cases = [
(10, 5, 'add'), # 正常
(10, 0, 'divide'), # 除零错误
(10, 5, 'invalid'), # 无效操作
('10', 5, 'add'), # 类型错误
]
print("=== 测试异常记录 ===")
for x, y, op in test_cases:
try:
result = risky_calculation(x, y, op)
print(f"结果: {result}")
except Exception as e:
print(f"操作失败: {e}")
print()
上下文管理器的实现
class FileManager:
"""自定义文件管理器上下文管理器"""
def __init__(self, filename, mode='r'):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
"""进入上下文时调用"""
print(f"打开文件: {self.filename}")
try:
self.file = open(self.filename, self.mode, encoding='utf-8')
return self.file
except Exception as e:
print(f"打开文件失败: {e}")
raise
def __exit__(self, exc_type, exc_value, traceback):
"""退出上下文时调用"""
if self.file:
print(f"关闭文件: {self.filename}")
self.file.close()
# 如果发生异常,记录异常信息
if exc_type isnotNone:
print(f"处理文件时发生异常: {exc_type.__name__}: {exc_value}")
returnFalse# 不抑制异常
returnTrue
# 使用自定义上下文管理器
print("=== 测试自定义上下文管理器 ===")
# 正常使用
try:
with FileManager('test_context.txt', 'w') as f:
f.write("使用自定义上下文管理器写入的内容")
print("文件写入成功")
except Exception as e:
print(f"操作失败: {e}")
# 读取文件
try:
with FileManager('test_context.txt', 'r') as f:
content = f.read()
print(f"读取内容: {content}")
except Exception as e:
print(f"读取失败: {e}")
# 尝试打开不存在的文件
try:
with FileManager('nonexistent.txt', 'r') as f:
content = f.read()
except Exception as e:
print(f"操作失败: {e}")
10:第二天复盘与练习
重构单词统计脚本
让我们将第一周的单词统计脚本进行重构,应用本周学到的高级特性:
import time
import datetime
import logging
from functools import wraps
from pathlib import Path
import json
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('word_counter.log', encoding='utf-8'),
logging.StreamHandler()
]
)
class WordCounterError(Exception):
"""单词统计器自定义异常"""
pass
class FileProcessingError(WordCounterError):
"""文件处理错误"""
pass
def log_execution(func):
"""记录函数执行时间的装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
func_name = func.__name__
logging.info(f"开始执行函数: {func_name}")
try:
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
logging.info(f"函数 {func_name} 执行成功,耗时: {execution_time:.4f} 秒")
return result
except Exception as e:
end_time = time.time()
execution_time = end_time - start_time
logging.error(f"函数 {func_name} 执行失败,耗时: {execution_time:.4f} 秒")
logging.error(f"错误信息: {e}")
raise
return wrapper
def validate_file_path(func):
"""验证文件路径的装饰器"""
@wraps(func)
def wrapper(file_path, *args, **kwargs):
path = Path(file_path)
ifnot path.exists():
raise FileProcessingError(f"文件不存在: {file_path}")
ifnot path.is_file():
raise FileProcessingError(f"路径不是文件: {file_path}")
if path.suffix.lower() notin ['.txt', '.md', '.csv']:
logging.warning(f"文件类型可能不受支持: {path.suffix}")
return func(file_path, *args, **kwargs)
return wrapper
class AdvancedWordCounter:
"""高级单词统计器"""
def __init__(self, stopwords=None, min_word_length=1):
"""
初始化单词统计器
stopwords: 停用词集合
min_word_length: 最小单词长度
"""
self.stopwords = stopwords or self._get_default_stopwords()
self.min_word_length = min_word_length
self.statistics = {}
def _get_default_stopwords(self):
"""获取默认停用词"""
return {
'的', '了', '在', '是', '我', '有', '和', '就', '不', '人',
'都', '一', '一个', '上', '也', '很', '到', '说', '要', '去',
'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or',
'but', 'in', 'with', 'to', 'for', 'of', 'as', 'by'
}
@log_execution
@validate_file_path
def process_file(self, file_path, encoding='utf-8'):
"""
处理文件,统计单词频率
file_path: 文件路径
encoding: 文件编码
"""
word_count = {}
line_count = 0
total_chars = 0
try:
# 使用生成器逐行处理文件
for line_num, line in self._read_file_lines(file_path, encoding):
line_count += 1
total_chars += len(line)
# 处理每一行的单词
for word in self._extract_words(line):
if self._is_valid_word(word):
word_count[word] = word_count.get(word, 0) + 1
except UnicodeDecodeError as e:
raise FileProcessingError(f"文件编码错误: {e}")
except Exception as e:
raise FileProcessingError(f"处理文件时发生错误: {e}")
# 保存统计信息
self.statistics = {
'word_count': word_count,
'total_words': sum(word_count.values()),
'unique_words': len(word_count),
'line_count': line_count,
'char_count': total_chars,
'file_path': str(file_path),
'processing_time': datetime.datetime.now().isoformat()
}
return word_count
def _read_file_lines(self, file_path, encoding):
"""生成器:逐行读取文件"""
try:
with open(file_path, 'r', encoding=encoding) as file:
for line_num, line in enumerate(file, 1):
yield line_num, line.strip()
except FileNotFoundError:
raise FileProcessingError(f"文件不存在: {file_path}")
except PermissionError:
raise FileProcessingError(f"没有权限读取文件: {file_path}")
def _extract_words(self, line):
"""从行中提取单词"""
# 移除标点符号并分割单词
import re
# 保留中文、英文、数字
words = re.findall(r'[a-zA-Z\u4e00-\u9fff0-9]+', line.lower())
return words
def _is_valid_word(self, word):
"""判断单词是否有效"""
return (
len(word) >= self.min_word_length and
word notin self.stopwords
)
@log_execution
def get_top_words(self, n=10):
"""获取出现频率最高的N个单词"""
ifnot self.statistics.get('word_count'):
raise WordCounterError("请先处理文件")
word_count = self.statistics['word_count']
sorted_words = sorted(
word_count.items(),
key=lambda x: x[1],
reverse=True
)
return sorted_words[:n]
@log_execution
def save_results(self, output_file):
"""保存结果到文件"""
ifnot self.statistics:
raise WordCounterError("没有统计结果可保存")
try:
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(self.statistics, f, ensure_ascii=False, indent=2)
logging.info(f"结果已保存到: {output_file}")
except Exception as e:
raise FileProcessingError(f"保存结果失败: {e}")
def generate_report(self):
"""生成统计报告"""
ifnot self.statistics:
return"没有统计数据"
stats = self.statistics
top_words = self.get_top_words(10)
# 使用生成器创建报告行
def report_lines():
yield"=" * 50
yield"单词频率统计报告"
yield"=" * 50
yieldf"文件路径: {stats['file_path']}"
yieldf"处理时间: {stats['processing_time']}"
yieldf"总行数: {stats['line_count']:,}"
yieldf"总字符数: {stats['char_count']:,}"
yieldf"总单词数: {stats['total_words']:,}"
yieldf"唯一单词数: {stats['unique_words']:,}"
yield""
yield"出现频率最高的10个单词:"
yield"-" * 30
for i, (word, count) in enumerate(top_words, 1):
percentage = (count / stats['total_words']) * 100
yieldf"{i:2d}. {word:<15} {count:>6}次 ({percentage:.2f}%)"
yield"=" * 50
return'\n'.join(report_lines())
def create_sample_files():
"""创建示例文件用于测试"""
# 中文示例文件
chinese_content = """
Python是一种广泛使用的解释型、高级和通用的编程语言。Python支持多种编程范式,
包括面向对象、命令式、函数式和过程式编程。它拥有动态类型系统和垃圾回收功能,
能够自动管理内存使用,并且具有一个庞大而全面的标准库。Python由Guido van Rossum创建,
第一版发布于1991年。Python的设计哲学强调代码的可读性和简洁的语法,尤其是使用空格缩进来划分代码块。
Python编程语言经常被称为胶水语言,它可以把用其他语言制作的各种模块连接在一起。
"""
# 英文示例文件
english_content = """
Python is a high-level, interpreted programming language with dynamic semantics.
Its high-level built-in data structures, combined with dynamic typing and dynamic binding,
make it very attractive for Rapid Application Development, as well as for use as a scripting
or glue language to connect existing components together. Python's simple, easy to learn
syntax emphasizes readability and therefore reduces the cost of program maintenance.
Python supports modules and packages, which encourages program modularity and code reuse.
"""
with open('chinese_sample.txt', 'w', encoding='utf-8') as f:
f.write(chinese_content)
with open('english_sample.txt', 'w', encoding='utf-8') as f:
f.write(english_content)
def main():
"""主函数:演示完整的单词统计流程"""
# 创建示例文件
create_sample_files()
# 创建单词统计器
counter = AdvancedWordCounter(min_word_length=2)
# 测试文件列表
test_files = ['chinese_sample.txt', 'english_sample.txt']
for file_path in test_files:
try:
print(f"\n处理文件: {file_path}")
print("=" * 60)
# 处理文件
word_count = counter.process_file(file_path)
# 生成并显示报告
report = counter.generate_report()
print(report)
# 保存结果
output_file = f"result_{Path(file_path).stem}.json"
counter.save_results(output_file)
except WordCounterError as e:
logging.error(f"处理文件 {file_path} 时发生错误: {e}")
except Exception as e:
logging.error(f"未知错误: {e}")
# 测试错误处理
print("\n测试错误处理:")
print("=" * 60)
try:
counter.process_file('nonexistent.txt')
except FileProcessingError as e:
print(f"捕获到文件处理错误: {e}")
if __name__ == "__main__":
main()
学习总结与最佳实践
通过今天的学习,我们掌握了Python的高级特性:
-
函数作为一等公民: 理解了函数可以像变量一样传递和使用
-
闭包与装饰器: 学会了如何增强函数功能而不修改原代码
-
生成器与迭代器: 掌握了内存友好的数据处理方式
-
文件与异常处理: 学会了安全、健壮的文件操作和错误处理
🌟 小贴士:还是那句话,AI编程时代,已经不需要完全动手写代码,了解所有的语法,重点是理解含义,能看懂即可,不需要记语法!不需要记语法!不需要记语法!
本文使用 文章同步助手 同步