在Python开发中,模块是代码组织的基本单元。每个.py文件都是一个独立的模块,这种设计让Python项目能够以"乐高积木"的方式构建。模块主要分为两类:
模块的核心价值在于:
实际开发中,当单个.py文件超过300行代码时,就应该考虑拆分为模块。我个人的经验法则是:一个模块应该只解决一个特定问题。
创建一个实用的自定义模块需要注意以下要点:
命名规范:
典型模块结构示例:
python复制# my_utils.py
"""模块功能说明文档(这是重要的注释习惯)"""
# 常量定义
MAX_RETRIES = 3
TIMEOUT = 30
# 工具函数
def format_size(bytes):
"""将字节数转换为易读格式"""
for unit in ['', 'K', 'M', 'G']:
if bytes < 1024:
return f"{bytes:.2f}{unit}B"
bytes /= 1024
# 工具类
class Logger:
def __init__(self, filename):
self.filename = filename
def write(self, message):
with open(self.filename, 'a') as f:
f.write(f"{datetime.now()}: {message}\n")
导入方式对比:
| 导入方式 | 语法 | 适用场景 | 注意事项 |
|---|---|---|---|
| 基本导入 | import module |
需要频繁使用模块内多个对象 | 使用时需带模块名前缀 |
| 别名导入 | import module as m |
模块名较长时 | 别名应保持语义清晰 |
| 选择性导入 | from module import func |
只需使用特定函数 | 可能引发命名冲突 |
| 全量导入 | from module import * |
快速原型开发 | 生产环境不推荐使用 |
实际开发建议:
if __name__ == '__main__'中当项目规模扩大时,需要用包(Package)来组织模块。包的本质是一个包含__init__.py的目录,这个特殊文件有以下作用:
from package import *的行为创建包的推荐结构:
code复制my_package/
├── __init__.py
├── utils/
│ ├── __init__.py
│ ├── file_utils.py
│ └── network_utils.py
├── core/
│ ├── __init__.py
│ └── processor.py
└── tests/
├── __init__.py
└── test_utils.py
__init__.py的高级用法:
python复制# 控制导入范围
__all__ = ['module1', 'module2']
# 包级别初始化
CONFIG = {'debug': False}
# 提供快捷导入路径
from .core.processor import MainProcessor
相对导入的注意事项:
Python通过内置的open函数进行文件操作,其完整签名如下:
python复制open(
file, # 文件路径
mode='r', # 打开模式
buffering=-1, # 缓冲策略
encoding=None, # 编码格式
errors=None, # 编解码错误处理
newline=None, # 换行符处理
closefd=True, # 是否关闭底层描述符
opener=None # 自定义开启器
)
模式参数详解:
| 模式 | 描述 | 文件存在 | 文件不存在 | 指针位置 | 是否覆盖 |
|---|---|---|---|---|---|
| 'r' | 只读 | 正常打开 | 抛出异常 | 开头 | 否 |
| 'w' | 只写 | 清空文件 | 创建新文件 | 开头 | 是 |
| 'a' | 追加 | 正常打开 | 创建新文件 | 末尾 | 否 |
| 'x' | 排它创建 | 抛出异常 | 创建新文件 | 开头 | 否 |
| 'b' | 二进制模式 | 与其他模式组合使用 | - | - | - |
| '+' | 读写模式 | 与其他模式组合使用 | - | - | - |
编码问题实战建议:
Python中有三种处理路径的方式:
字符串拼接(不推荐)
python复制path = 'data/' + filename # 跨平台问题
os.path模块(传统方式)
python复制import os
path = os.path.join('data', 'subdir', filename)
pathlib(Python 3.4+推荐)
python复制from pathlib import Path
path = Path('data') / 'subdir' / filename
路径操作对照表:
| 操作 | os.path方式 | pathlib方式 |
|---|---|---|
| 路径拼接 | os.path.join(a, b) | Path(a) / b |
| 获取父目录 | os.path.dirname(p) | p.parent |
| 获取文件名 | os.path.basename(p) | p.name |
| 获取后缀 | os.path.splitext(p)[1] | p.suffix |
| 判断存在 | os.path.exists(p) | p.exists() |
| 是否是文件 | os.path.isfile(p) | p.is_file() |
在新项目中应优先使用pathlib,它提供更面向对象的API,且能自动处理不同操作系统的路径差异。
上下文管理器(推荐方式):
python复制with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
# 自动处理文件关闭
大文件读取策略:
逐行读取(内存友好)
python复制with open('large.log') as f:
for line in f: # 文件对象是可迭代的
process(line)
固定大小块读取
python复制CHUNK_SIZE = 4096
with open('video.mp4', 'rb') as f:
while chunk := f.read(CHUNK_SIZE):
process_chunk(chunk)
常见文件操作问题排查:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| PermissionError | 无读写权限 | 检查文件权限位 |
| FileNotFoundError | 路径错误 | 检查路径是否存在 |
| UnicodeDecodeError | 编码不匹配 | 指定正确编码 |
| IsADirectoryError | 误操作目录 | 检查路径类型 |
os模块提供了丰富的文件和目录操作方法:
常用文件操作:
python复制import os
# 重命名文件
os.rename('old.txt', 'new.txt')
# 删除文件
os.remove('obsolete.txt')
# 获取文件大小(字节)
size = os.path.getsize('data.bin')
# 获取修改时间(时间戳)
mtime = os.path.getmtime('config.ini')
目录操作完整示例:
python复制def clean_old_files(dir_path, days=30):
"""删除目录中超过指定天数的文件"""
cutoff = time.time() - days * 86400
for filename in os.listdir(dir_path):
filepath = os.path.join(dir_path, filename)
if os.path.isfile(filepath):
if os.path.getmtime(filepath) < cutoff:
print(f"Deleting {filename}")
os.remove(filepath)
shutil模块提供了更高级的文件操作功能:
文件复制对比:
python复制import shutil
# 简单复制(不保留元数据)
shutil.copy('src.txt', 'dst.txt')
# 完整复制(保留权限、时间等)
shutil.copy2('src.txt', 'backup/src.txt')
# 递归复制目录
shutil.copytree('src_dir', 'backup_dir')
# 移动文件/目录
shutil.move('old_location', 'new_location')
磁盘空间检查:
python复制usage = shutil.disk_usage('/')
print(f"Total: {usage.total / (1024**3):.1f}GB")
print(f"Used: {usage.used / (1024**3):.1f}GB")
print(f"Free: {usage.free / (1024**3):.1f}GB")
tempfile模块可以安全地创建临时文件:
python复制import tempfile
# 创建临时文件(自动删除)
with tempfile.NamedTemporaryFile(delete=True) as tmp:
tmp.write(b"temp data")
tmp.seek(0)
print(tmp.read())
# 创建临时目录
with tempfile.TemporaryDirectory() as tmpdir:
print(f"Working in {tmpdir}")
# 目录使用后自动删除
缓冲策略选择:
批量写入优化:
python复制# 不佳实践(多次IO操作)
with open('log.txt', 'a') as f:
for event in events:
f.write(f"{event}\n")
# 优化方案(单次IO操作)
with open('log.txt', 'a') as f:
f.writelines(f"{e}\n" for e in events)
路径分隔符处理:
python复制# 错误方式(硬编码分隔符)
path = 'data\\subdir\\file.txt' # Windows only
# 正确方式
path = os.path.join('data', 'subdir', 'file.txt')
path = Path('data') / 'subdir' / 'file.txt'
换行符处理:
python复制# 统一换行符处理
with open('output.txt', 'w', newline='\n') as f:
f.write(content) # 始终使用\n作为换行符
健壮的文件操作模板:
python复制import os
from pathlib import Path
def safe_file_operation(file_path):
try:
path = Path(file_path)
if not path.exists():
raise FileNotFoundError(f"{file_path} does not exist")
with open(path, 'r', encoding='utf-8') as f:
content = f.read()
# 处理文件内容
return process_content(content)
except PermissionError:
print(f"Permission denied: {file_path}")
except UnicodeDecodeError:
print(f"Encoding error in {file_path}")
except OSError as e:
print(f"OS error occurred: {e}")
finally:
print(f"Finished processing {file_path}")
pathlib.Path提供了更直观的操作方式:
常见操作示例:
python复制from pathlib import Path
# 创建目录(包括父目录)
data_dir = Path('data') / 'project'
data_dir.mkdir(parents=True, exist_ok=True)
# 查找特定文件
for py_file in Path('src').glob('**/*.py'):
print(f"Found Python file: {py_file}")
# 读写文件内容
config = Path('config.json')
if config.exists():
settings = json.loads(config.read_text())
使用watchdog库实现文件监控:
python复制from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class MyHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith('.py'):
print(f"Python file changed: {event.src_path}")
observer = Observer()
observer.schedule(MyHandler(), path='.', recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
使用mmap处理超大文件:
python复制import mmap
with open('huge.data', 'r+b') as f:
# 内存映射文件
with mmap.mmap(f.fileno(), 0) as mm:
# 像操作字符串一样操作文件
if mm.find(b'important') != -1:
mm.seek(0)
header = mm.readline()
print(f"Header: {header.decode()}")
在实际项目中,我发现合理组合这些技术可以构建出既高效又健壮的文件处理系统。比如在日志处理系统中,使用内存映射处理大日志文件,配合watchdog实现实时监控,再用pathlib处理跨平台路径问题,这样的组合能解决大多数文件处理需求。