别再只用os.listdir了!Python文件遍历,用glob模块这5个技巧更高效
解锁Python文件遍历新姿势:glob模块的5个高阶技巧
在Python开发者的日常工作中,文件遍历几乎是无法绕开的任务。无论是日志分析、数据集整理还是自动化测试,我们都需要频繁地与文件系统打交道。很多开发者习惯性地使用os.listdir()或手动递归来处理这些需求,却不知道Python标准库中隐藏着一个更强大的工具——glob模块。
1. 为什么glob比传统方法更值得选择?
1.1 os.listdir的局限性
os.listdir()是大多数Python开发者接触的第一个文件遍历方法。它简单直接,返回指定路径下的所有文件和子目录列表。但在实际项目中,我们很快就会发现它的不足:
import os # 基本用法 files = os.listdir('data/') print(files) # 输出所有文件和目录,需要额外处理才能区分主要问题包括:
- 无法直接过滤文件类型
- 需要手动处理路径拼接
- 不支持递归遍历子目录
- 缺乏模式匹配能力
1.2 os.walk的复杂性
当需要递归遍历目录时,很多开发者会转向os.walk():
for root, dirs, files in os.walk('data/'): for file in files: if file.endswith('.csv'): print(os.path.join(root, file))虽然功能强大,但存在以下痛点:
- 代码冗长,需要多层循环
- 过滤逻辑需要手动实现
- 返回结构复杂(三个列表)
- 模式匹配能力有限
1.3 glob的优雅解决方案
相比之下,glob模块提供了更简洁的API:
import glob # 简单匹配 csv_files = glob.glob('data/*.csv') print(csv_files) # 直接得到匹配的完整路径列表 # 递归匹配 all_csv = glob.glob('data/**/*.csv', recursive=True)优势对比表:
| 特性 | os.listdir | os.walk | glob |
|---|---|---|---|
| 单层遍历 | ✓ | ✓ | ✓ |
| 递归遍历 | ✗ | ✓ | ✓ |
| 内置模式匹配 | ✗ | ✗ | ✓ |
| 路径自动拼接 | ✗ | ✓ | ✓ |
| 代码简洁度 | 中等 | 复杂 | 简单 |
| 返回结果直接可用度 | 低 | 中 | 高 |
2. glob的5个高阶技巧
2.1 递归通配符:一键遍历所有子目录
**是glob中最强大的通配符之一,配合recursive=True参数可以实现全目录递归搜索:
# 查找项目中的所有Python文件 py_files = glob.glob('**/*.py', recursive=True) # 查找特定子目录下的图片 images = glob.glob('static/**/*.jpg', recursive=True)注意:在Windows系统中,路径分隔符会自动转换为反斜杠,但模式匹配中应始终使用正斜杠(/)
2.2 字符集匹配:精准定位特定文件
glob支持类似正则表达式的字符集匹配,但语法更简单:
# 匹配log2021.log到log2029.log decade_logs = glob.glob('logs/log202[1-9].log') # 匹配test1.py到test9.py single_digit_tests = glob.glob('tests/test[1-9].py') # 匹配a.txt或b.txt但不包括c.txt select_files = glob.glob('data/[ab].txt')字符集规则:
[abc]:匹配a、b或c[a-z]:匹配任何小写字母[0-9]:匹配任何数字[!a]:匹配非a的字符
2.3 问号通配符:固定长度模糊匹配
当你知道文件名长度但不确定具体字符时,?通配符非常有用:
# 匹配所有3字符名称的CSV文件 three_char_csv = glob.glob('data/???.csv') # 匹配img_后面跟2个字符的PNG图片 specific_images = glob.glob('images/img_??.png')2.4 组合模式:构建复杂匹配逻辑
通过组合不同的通配符,可以创建复杂的匹配模式:
# 匹配2020-2029年每月的数据文件 yearly_data = glob.glob('data/202[0-9]-[01][0-9].csv') # 匹配以test开头,接着是1-5的数字,最后是_a或_b的.py文件 complex_test = glob.glob('tests/test[1-5]_[ab].py')2.5 与pathlib结合:面向对象的优雅操作
Python 3.4+引入了pathlib模块,它与glob完美配合:
from pathlib import Path # 使用Path对象的glob方法 py_files = list(Path('.').glob('**/*.py')) # 更复杂的匹配 images = list(Path('static').glob('*.[pj][np]g')) # 匹配.png和.jpgpathlib+glob的优势:
- 链式调用更流畅
- 路径操作更安全
- 返回的是Path对象而非字符串
- 跨平台兼容性更好
3. 性能优化与实战技巧
3.1 缓存机制提升重复查询速度
对于需要多次执行相同glob模式的情况,可以预先编译模式:
import glob import fnmatch pattern = 'data/*.csv' matcher = fnmatch.translate(pattern) # 转换为正则表达式 compiled = re.compile(matcher) # 后续使用编译后的模式进行匹配 [csv for csv in os.listdir('data') if compiled.match(csv)]3.2 处理大型目录结构的策略
当处理包含数万文件的目录时,可以考虑:
使用
iglob替代glob获取生成器而非列表:large_files = glob.iglob('big_data/**/*.log', recursive=True) for file in large_files: process(file)分批次处理:
batch_size = 1000 files = list(glob.iglob('huge_dir/**/*.json', recursive=True)) for i in range(0, len(files), batch_size): batch = files[i:i+batch_size] process_batch(batch)
3.3 常见陷阱与解决方案
问题1:隐藏文件被忽略
glob默认不匹配以点开头的文件(Unix隐藏文件),解决方法:
# 匹配包括隐藏文件在内的所有文件 all_files = glob.glob('.*') + glob.glob('*')问题2:跨平台路径分隔符
Windows使用反斜杠而Unix使用正斜杠,最佳实践:
# 总是使用正斜杠编写模式 files = glob.glob('data/**/*.csv', recursive=True) # 需要处理路径时使用os.path或pathlib import os.path full_path = os.path.join('data', 'subdir', 'file.csv')问题3:符号链接循环
递归遍历时可能遇到符号链接导致的无限循环,防护措施:
def safe_glob(pattern): seen = set() for file in glob.iglob(pattern, recursive=True): real_path = os.path.realpath(file) if real_path not in seen: seen.add(real_path) yield file4. 真实项目应用案例
4.1 日志分析系统
假设我们需要分析分布在多个目录中的服务器日志:
def analyze_logs(): log_patterns = [ '/var/log/app/*.log', '/var/log/app/archive/**/*.log', '/var/log/app/*/error_*.log' ] for pattern in log_patterns: for log_file in glob.iglob(pattern, recursive=True): with open(log_file) as f: process_log(f.read())4.2 图片资源整理
整理散落在不同目录的图片资源:
def organize_images(target_dir='organized_images'): image_exts = ['*.jpg', '*.png', '*.gif'] os.makedirs(target_dir, exist_ok=True) for ext in image_exts: for img_path in glob.iglob(f'**/{ext}', recursive=True): date = get_image_date(img_path) # 假设的函数 dest_dir = os.path.join(target_dir, date) os.makedirs(dest_dir, exist_ok=True) shutil.copy2(img_path, dest_dir)4.3 自动化测试发现
动态发现并运行测试用例:
def discover_tests(): test_files = glob.glob('tests/**/test_*.py', recursive=True) for file in test_files: module_name = file.replace('/', '.').replace('\\', '.')[:-3] __import__(module_name) # 动态导入测试模块4.4 数据管道构建
构建数据处理管道时收集输入文件:
class DataPipeline: def __init__(self, input_patterns): self.input_files = [] for pattern in input_patterns: self.input_files.extend(glob.glob(pattern, recursive=True)) def process(self): for file in self.input_files: data = load_data(file) # 假设的数据加载函数 transformed = transform(data) save_results(transformed)5. 进阶模式与替代方案
5.1 自定义匹配函数
当内置模式不能满足需求时,可以结合过滤函数:
def find_recent_files(pattern, days=7): now = time.time() cutoff = now - days * 86400 for file in glob.iglob(pattern, recursive=True): if os.path.getmtime(file) >= cutoff: yield file5.2 与正则表达式结合
对于更复杂的匹配需求,可以将glob与re模块结合:
import re def glob_re(pattern, string): # 将glob模式转换为正则表达式 regex = fnmatch.translate(pattern) return re.fullmatch(regex, string) is not None # 使用示例 files = [f for f in os.listdir() if glob_re('data_[0-9][0-9].csv', f)]5.3 替代方案对比
虽然glob很强大,但某些场景下其他工具可能更合适:
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 极大量文件(百万+) | os.scandir | 内存效率更高 |
| 需要文件元信息 | pathlib | 直接获取stat信息 |
| 复杂条件过滤 | os.listdir+filter | 更灵活的编程控制 |
| 实时监控文件系统变化 | watchdog | 专门的文件系统事件监控库 |
| 跨平台特殊字符处理 | pathlib | 自动处理平台差异 |
