Python获取文件大小:四种方法原理、性能对比与最佳实践

发布时间:2026/7/31 5:38:31
Python获取文件大小:四种方法原理、性能对比与最佳实践 1. 项目概述为什么我们需要关心文件大小在Python里处理文件获取文件大小这个操作听起来简单得不能再简单了对吧但就是这么个基础操作在实际项目中却无处不在而且不同的场景下选择哪种方法还真有点讲究。我遇到过不少新手甚至一些有经验的开发者在处理文件时要么是性能上吃了亏要么是代码写得不够优雅要么是在跨平台兼容性上栽了跟头。想想这些场景你写一个文件上传接口需要在前端就限制用户上传文件的大小或者在服务器端进行二次校验你开发一个日志分析工具需要监控日志文件的增长在超过某个阈值时进行切割或告警你写一个备份脚本需要统计备份目录的总大小以决定是否要清理旧数据甚至你在做自动化测试时需要验证生成的文件是否符合预期的尺寸。所有这些第一步都是获取文件的大小。Python提供了不止一种方法来完成这个任务从经典的os.path.getsize到更底层的os.stat再到面向对象的pathlib甚至还有直接读取文件对象的方式。每种方法背后都有其设计哲学和适用场景。这篇文章我就结合自己十多年的踩坑经验把这四种主流方法掰开揉碎了讲清楚告诉你它们各自的原理、性能差异、使用时的“坑”以及在不同情况下我个人会怎么选。目标很简单让你看完之后不仅能写出能跑的代码更能写出高效、健壮、优雅的代码。2. 四种核心方法深度解析与横向对比获取文件大小本质上就是向操作系统询问一个文件的元数据metadata。在Unix/Linux和Windows系统中文件大小是文件元数据如inode或文件记录中的一个标准字段。Python的几种方法都是对这个系统调用的不同层次的封装。为了有一个直观的认识我们先创建一个测试文件并快速浏览一下这四种方法的基本用法。假设我们有一个名为test_file.txt的文件。# 快速示例四种方法的基本形态 import os from pathlib import Path file_path ‘test_file.txt’ # 方法1: os.path.getsize size1 os.path.getsize(file_path) print(f“os.path.getsize: {size1} bytes”) # 方法2: os.stat stat_info os.stat(file_path) size2 stat_info.st_size print(f“os.stat: {size2} bytes”) # 方法3: pathlib.Path path_obj Path(file_path) size3 path_obj.stat().st_size # 方式3.1 类似 os.stat size4 path_obj.stat().st_size # 注意pathlib.Path 没有直接的 getsize 但 .stat() 是标准方式 # 更地道的 pathlib 写法是 size3_proper path_obj.stat().st_size print(f“pathlib.Path.stat(): {size3_proper} bytes”) # 方法4: 通过文件对象 with open(file_path, ‘rb’) as f: f.seek(0, 2) # 将指针移动到文件末尾 size4 f.tell() # 获取当前指针位置即文件大小 print(f“File object seek/tell: {size4} bytes”)看起来都能得到结果但内在的区别大了去了。下面我们逐一深入。2.1 方法一os.path.getsize – 简洁的“快捷方式”os.path.getsize(path)可能是最广为人知的方法。它属于os.path模块这个模块包含了很多用于处理路径名的函数。原理与底层实现在大多数Python实现如CPython中os.path.getsize本质上是对os.stat的一个简单封装。你可以近似地把它理解为def getsize(path): return os.stat(path).st_size它内部调用了os.stat()函数获取文件的完整状态信息然后只返回其中的st_size字段。这是一个同步阻塞调用会直接向操作系统发起请求。使用示例与特点import os file_path ‘/home/user/data/report.pdf’ try: file_size os.path.getsize(file_path) print(f“文件大小为: {file_size} 字节”) print(f“约为: {file_size / 1024 / 1024:.2f} MB”) # 转换为MB except FileNotFoundError: print(“文件不存在”) except OSError as e: print(f“访问文件时出错: {e}”)优点接口极其简洁函数名即功能一目了然学习成本几乎为零。直接返回目标值你不需要从一堆元数据中手动提取st_size。缺点与注意事项功能单一它只返回大小。如果你还需要文件的修改时间、访问权限等其他信息你就得再调用一次os.stat造成重复的系统调用影响性能。错误处理它可能抛出多种异常如FileNotFoundError文件不存在、PermissionError权限不足等必须用try…except块妥善处理。符号链接Symbolic Link这是一个关键点os.path.getsize跟随符号链接。也就是说如果path是一个指向另一个文件的软链接该方法返回的是目标文件的大小而不是链接文件本身的大小链接文件本身很小只存储一个路径。在某些需要处理链接本身的场景下这可能不是你想要的行为。实操心得os.path.getsize就像一把瑞士军刀里的开瓶器干一件特定的事非常顺手。在快速脚本、一次性任务或者你百分之百确定只需要文件大小且不关心符号链接的场景下它是首选。但在严肃的、可能涉及性能或需要更多元数据的生产代码中我通常会考虑其他方法。2.2 方法二os.stat – 强大而底层的“信息库”os.stat(path)是更底层、更强大的工具。它返回一个os.stat_result对象这个对象包含了文件或文件描述符的所有状态信息。原理与信息深度os.stat直接对应着操作系统底层的stat()系统调用在Windows上是类似的机制。这个调用会获取文件inode或类似结构中的所有信息。返回的os.stat_result对象是一个命名元组包含多个属性属性描述常见用途st_mode文件类型和权限位mode bits判断是否为文件、目录检查读写权限st_inoinode 编号文件系统内部标识可用于硬链接判断st_dev设备ID标识文件所在的设备st_nlink硬链接数量st_uid所有者的用户ID权限管理st_gid所有者的组ID权限管理st_size文件大小以字节为单位我们关注的核心st_atime最近访问时间秒缓存策略日志分析st_mtime最近修改时间秒判断文件是否更新触发构建st_ctime元数据修改时间秒在Unix上通常是inode变更时间使用示例与进阶技巧import os import time file_path ‘/var/log/syslog’ try: stat_info os.stat(file_path) # 获取大小 size stat_info.st_size print(f“大小: {size} bytes”) # 同时获取其他有用信息 print(f“最后修改时间: {time.ctime(stat_info.st_mtime)}”) print(f“文件模式: {oct(stat_info.st_mode)}”) # 以八进制显示权限 print(f“是否为文件: {os.path.isfile(file_path)}”) # 结合其他判断 # 判断文件是否过大例如超过100MB if size 100 * 1024 * 1024: print(“警告文件超过100MB建议进行切割或清理。”) except FileNotFoundError: print(“日志文件不存在可能尚未创建或已被轮转。”) except PermissionError: print(“权限不足无法读取系统日志。请尝试使用sudo或检查用户组。”)处理符号链接os.stat()vsos.lstat()这是os.stat相关的一个重要知识点。默认的os.stat()会跟随符号链接就像os.path.getsize一样。如果你需要获取符号链接本身的信息其大小通常只是存储的路径字符串的长度你需要使用os.lstat()。import os # 创建一个指向文件的符号链接 os.symlink(‘/path/to/target_file’, ‘my_link’) info_stat os.stat(‘my_link’) # 返回的是 target_file 的信息 info_lstat os.lstat(‘my_link’) # 返回的是 my_link 本身的信息 print(f“os.stat size (target): {info_stat.st_size}“) print(f“os.lstat size (link itself): {info_lstat.st_size}“) # 这个值很小优点信息全面一次调用获取所有元数据避免为获取不同信息而多次调用系统函数效率更高。功能强大通过st_mode可以判断文件类型通过时间戳可以实现复杂的文件监控逻辑。灵活性高可以选择使用stat()或lstat()来处理符号链接。缺点与注意事项接口稍显复杂需要从结果对象中提取st_size多了一步操作。需要记忆属性名虽然属性名是标准的但新手可能需要查阅文档。时间戳精度注意st_atime,st_mtime,st_ctime在不同操作系统上的精度和更新时机可能不同例如某些系统为了性能可能不会实时更新st_atime。实操心得os.stat是我在生产环境代码中最常用的方法。尤其是在需要同时获取文件大小和修改时间来判断文件是否变化的场景比如实现一个简单的文件监视器或者需要检查文件权限时。一次系统调用拿到全部信息从性能和维护性上看通常都优于多次调用单一功能的函数。记住这个原则如果除了大小你很可能还需要其他信息那就直接用os.stat。2.3 方法三pathlib.Path – 面向对象的“现代派”pathlib模块从Python 3.4开始成为标准库的一部分它提供了一种面向对象的、更符合直觉的文件系统路径操作方法。对于从其他现代语言如C#、Java转过来的开发者来说pathlib的API设计会感觉非常亲切。面向对象的设计哲学pathlib的核心是Path类。你创建一个Path对象这个对象就代表了一条路径。然后你可以调用这个对象的各种方法来操作路径和文件。这种风格将路径从普通的字符串提升为“一等公民”可以链式调用方法代码更清晰。获取文件大小的方式Path对象本身没有getsize方法。要获取大小你需要先获取文件的stat信息然后再取大小。这其实和os.stat的流程一致但写法更面向对象。使用示例与链式调用from pathlib import Path # 创建Path对象 log_file Path(‘/var/log/app.log’) backup_dir Path(‘/backups’) # 检查是否存在并获取大小 if log_file.is_file(): # 使用对象方法判断是否为文件 stat_result log_file.stat() # 获取stat对象 size stat_result.st_size print(f“{log_file} 的大小是 {size} 字节”) # 更简洁的链式写法在一行内完成 size_chain log_file.stat().st_size print(f“链式调用大小: {size_chain} bytes”) # 同时利用其他信息 if stat_result.st_mtime (time.time() - 3600): # 一小时内有修改 print(“日志文件最近非常活跃”) # 结合glob模式计算目录下所有.txt文件的总大小 total_size 0 for txt_file in backup_dir.glob(‘*.txt’): if txt_file.is_file(): total_size txt_file.stat().st_size print(f“备份目录下所有txt文件总大小: {total_size} bytes”)处理符号链接Path对象同样区分stat()和lstat()。Path(‘symlink’).stat(): 跟随链接。Path(‘symlink’).lstat(): 不跟随链接获取链接本身信息。优点API设计优雅面向对象方法链式调用代码可读性高更符合现代编程习惯。路径操作安全方便拼接路径使用/操作符避免了字符串拼接可能带来的错误如忘记分隔符。# 传统方式 dir_path ‘/home/user’ file_name ‘data.txt’ full_path os.path.join(dir_path, file_name) # pathlib方式 dir_path Path(‘/home/user’) full_path dir_path / ‘data.txt’ # 更直观跨平台性更好pathlib内部自动处理了Windows和Unix路径分隔符的差异\vs/让你可以用统一的/风格写代码。功能集成度高Path对象集成了很多常用操作如exists(),is_file(),is_dir(),mkdir(),rename()等无需再导入os或os.path中的多个函数。缺点与注意事项性能微开销由于是面向对象的封装相比直接调用os.stat可能会有极其微小的性能开销但在99.9%的应用场景中完全可以忽略不计。不要过早优化代码的清晰度和可维护性更重要。Python版本要求需要Python 3.4。虽然现在新项目基本都满足但维护一些非常古老的系统时需要注意。习惯转变对于习惯了os.path的开发者需要一点时间来适应新的范式。实操心得对于所有新的Python项目我强烈推荐使用pathlib。它不仅仅是获取文件大小而是整体上让文件系统操作变得更安全、更愉悦。当你需要做的不仅仅是获取大小而是涉及路径拼接、文件检查、遍历目录等一系列操作时pathlib的优势会非常明显。把它看成是文件系统操作的“现代化改造”。2.4 方法四通过文件对象seek/tell– 特定场景的“偏方”这种方法比较“原始”打开文件将文件指针移动到末尾然后查询指针的位置这个位置就是文件的大小。原理剖析open()函数返回一个文件对象。文件对象内部维护着一个指向文件中某个位置的“指针”。f.seek(offset, whence)用于移动这个指针。whence2表示“相对于文件末尾”。f.seek(0, 2)就是将指针移动到距离文件末尾0字节的位置也就是文件末尾。f.tell()则返回指针当前在文件中的位置字节偏移量。因此在文件末尾调用tell()得到的就是文件的总大小。使用示例与细节def get_size_by_seek(filepath): “”” 通过 seek/tell 方法获取文件大小。 注意此方法会以读取模式打开文件。 “”” try: with open(filepath, ‘rb’) as f: # 必须用二进制模式 ‘rb’ f.seek(0, 2) # 移动到文件末尾 size f.tell() return size except FileNotFoundError: return None except IOError as e: print(f“无法打开文件 {filepath}: {e}“) return None size get_size_by_seek(‘large_data.bin’) if size is not None: print(f“文件大小: {size}“)为什么必须用二进制模式‘rb’在文本模式‘r’下由于存在字符编码转换如UTF-8的BOM头、换行符转换等文件指针的位置可能与实际的字节数不对应。seek()和tell()在文本模式下的行为是未定义的platform-dependent。二进制模式‘rb’保证了我们操作的是原始的字节流seek和tell的数值就是精确的字节偏移量。优点原理直观对于理解文件操作的基础文件指针有帮助。在某些极端情况下有用如果你已经因为其他原因比如要读取内容打开了文件那么顺便用这个方法获取大小可以避免额外的stat系统调用。但这种情况很少见。缺点与重大注意事项性能最差它需要打开文件这是一个相对昂贵的I/O操作比直接查询元数据的stat调用要慢得多尤其是对于网络文件系统或慢速存储设备。资源占用它占用了一个文件描述符。虽然在with语句中会自动关闭但在高并发或需要处理大量文件时无谓地打开文件会增加系统负担。可能引发副作用打开文件可能会触发文件锁在某些操作系统和文件系统上或者改变文件的“最近访问时间”st_atime这有时是你不想看到的。并非通用对于某些特殊文件如管道、设备文件/dev/null这种方法可能无法工作或返回无意义的结果。实操心得这是一个“反模式”除非有非常特殊的理由否则不要用。我几乎从未在生产代码中使用这种方法来专门获取文件大小。它的主要价值在于教学用于解释文件指针的概念。如果你看到别人的代码里用这个来获取文件大小可以考虑将其重构为使用os.stat或pathlib。记住一个黄金法则获取元数据就用元数据接口stat读取内容才去打开文件。3. 性能实测与场景化选型指南纸上谈兵不如实际跑一跑。我们来设计一个简单的性能测试看看这几种方法在效率上到底有多大差异。同时结合不同的应用场景给出我的选型建议。3.1 性能基准测试我们创建一个中等大小的文件比如10MB然后分别用四种方法获取其大小重复多次如1000次计算平均耗时。import os import timeit from pathlib import Path def create_test_file(size_mb10): “””创建一个指定大小的测试文件“”” file_name ‘perf_test.dat’ with open(file_name, ‘wb’) as f: f.write(os.urandom(size_mb * 1024 * 1024)) # 写入随机字节 return file_name def cleanup_test_file(file_name): “””清理测试文件“”” if os.path.exists(file_name): os.remove(file_name) def method_os_getsize(file_name): return os.path.getsize(file_name) def method_os_stat(file_name): return os.stat(file_name).st_size def method_pathlib(file_name): return Path(file_name).stat().st_size def method_seek_tell(file_name): with open(file_name, ‘rb’) as f: f.seek(0, 2) return f.tell() if __name__ ‘__main__’: test_file create_test_file(10) # 10MB文件 number 1000 # 执行次数 print(f“性能测试 (文件: {test_file}, 循环 {number} 次)“) print(“-” * 40) for func, name in [(method_os_getsize, ‘os.path.getsize’), (method_os_stat, ‘os.stat’), (method_pathlib, ‘pathlib.Path.stat’), (method_seek_tell, ‘seek/tell’)]: # 使用 timeit 进行精确计时避免第一次打开文件等开销的影响 timer timeit.Timer(lambda: func(test_file)) # 执行一次预热然后计算多次执行的平均时间 elapsed timer.timeit(numbernumber) / number print(f“{name:25} 平均耗时: {elapsed * 1e6:8.2f} 微秒”) cleanup_test_file(test_file)注意实际耗时因硬件、操作系统、Python版本而异以下为相对趋势分析在我的测试环境SSD Python 3.9下结果趋势通常是os.stat和os.path.getsize最快且速度几乎相同因为后者是前者的封装。pathlib.Path.stat()稍慢一点点差距在微秒级别可忽略不计。seek/tell方法显著慢一个数量级以上因为它涉及真正的文件I/O操作。这个测试印证了之前的理论分析基于stat的系统调用是最高效的方式。3.2 不同场景下的最佳实践选择知道了原理和性能我们来看看具体怎么选。没有绝对最好的只有最适合当前场景的。场景一写快速脚本或临时任务只需要文件大小推荐os.path.getsize理由代码最简短意图最清晰。你不需要导入pathlib也不需要处理stat_result对象。对于一次性脚本、命令行工具或简单的自动化任务这是最省脑细胞的选择。示例写一个脚本清理/tmp目录下超过100MB的旧文件。场景二开发生产级应用程序需要文件大小及其他元数据如修改时间、权限推荐os.stat或pathlib.Path.stat()理由一次调用获取全部信息性能最优。这是最专业、最通用的做法。如果项目是新的且广泛使用面向对象风格首选pathlib。file_path.stat().st_size的写法清晰且易于集成到更大的Path对象操作链中。如果项目是旧的或者你更习惯过程式编程或者你只需要在某个函数内部做一次简单的检查使用os.stat完全没问题。示例实现一个文件变化监控服务Watchdog需要持续检查文件的st_mtime和st_size。场景三需要处理符号链接本身推荐os.lstat()或Path.lstat()理由这是唯一能正确获取符号链接自身信息其大小是路径字符串长度的方法。os.path.getsize和默认的stat()都会跟随链接。示例写一个工具扫描目录统计其中所有实体包括链接文件的磁盘占用情况。场景四你已经以二进制模式打开了文件并且“顺便”需要知道大小可以考虑seek/tell理由虽然这种情况极少但如果你已经因为读取内容而打开了文件例如在解析文件头结构那么用f.seek(0,2); size f.tell()获取大小然后f.seek(0)回到开头可以避免一次额外的stat调用。但要非常小心确保文件是以二进制模式‘rb’打开的。示例解析一个自定义格式的二进制文件文件头没有存储大小信息你需要先知道总长度才能计算数据区偏移。场景五计算整个目录树的总大小推荐pathlib结合递归理由pathlib的Path.rglob()或Path.glob()非常适合进行递归文件遍历代码非常简洁。示例from pathlib import Path def get_dir_size(directory: Path) - int: total_size 0 for file_path in directory.rglob(‘*’): # rglob 递归遍历所有文件 if file_path.is_file() and not file_path.is_symlink(): # 跳过符号链接 try: total_size file_path.stat().st_size except OSError: # 忽略权限错误等问题 pass return total_size project_size get_dir_size(Path(‘./my_project’)) print(f“项目目录总大小: {project_size / 1e6:.2f} MB”)绝对不应该使用seek/tell的场景专门为了获取文件大小而写一个函数。在高频循环中获取大量文件的大小。处理可能被锁定的文件如某些数据库文件。处理特殊文件如设备文件。4. 常见问题、坑点排查与高级技巧即使知道了方法在实际编码中还是会遇到各种问题。下面是我总结的一些典型“坑”和解决技巧。4.1 文件不存在与权限问题这是最常遇到的运行时错误。问题表现FileNotFoundError: 路径错误或文件确实不存在。PermissionError: 当前用户对文件或父目录没有读取权限。解决方案永远进行防御性编程。使用try…except块捕获异常并提供友好的错误处理或回退方案。import os from pathlib import Path def safe_get_size(path): “”” 安全地获取文件大小处理常见异常。 返回文件大小字节如果出错则返回None。 “”” # 方法1: 使用 os.path.getsize 或 os.stat try: return os.path.getsize(path) except FileNotFoundError: print(f“错误文件 ‘{path}’ 不存在。”) return None except PermissionError: print(f“错误没有权限读取文件 ‘{path}’。”) return None except OSError as e: # 捕获其他操作系统错误 print(f“访问文件 ‘{path}’ 时发生未知错误: {e}“) return None # 方法2: 使用 pathlib (更推荐因为Path对象方法本身会抛出异常) # try: # return Path(path).stat().st_size # except FileNotFoundError: # … # except PermissionError: # … # 使用示例 size safe_get_size(‘/etc/shadow’) # 普通用户无权限 if size is None: print(“无法获取文件大小已记录错误。”) else: print(f“文件大小: {size}“)4.2 处理符号链接与硬链接这是一个容易混淆的概念对获取大小有直接影响。符号链接软链接是一个独立的文件内容是指向目标文件的路径。os.path.getsize(link)返回的是目标文件的大小。os.lstat(link).st_size返回的是链接文件本身即存储的路径字符串的大小通常很小。硬链接是同一个inode的多个目录条目。os.path.getsize(hardlink)和os.stat(hardlink).st_size返回的都是原始文件的大小因为所有硬链接都指向同一个数据块。st_nlink属性可以告诉你有多少个硬链接指向这个inode。技巧如何判断并正确处理import os from pathlib import Path def analyze_link(filepath): path Path(filepath) if path.is_symlink(): print(f“{filepath} 是一个符号链接。”) link_target path.readlink() print(f“它指向: {link_target}“) print(f“链接本身大小: {path.lstat().st_size} bytes”) print(f“目标文件大小: {path.stat().st_size} bytes”) elif os.stat(filepath).st_nlink 1: print(f“{filepath} 有 {os.stat(filepath).st_nlink} 个硬链接。”) print(f“文件大小: {os.path.getsize(filepath)} bytes”) else: print(f“{filepath} 是普通文件。”) print(f“文件大小: {os.path.getsize(filepath)} bytes”)4.3 大文件与整数溢出历史问题在早期32位系统上st_size是一个有符号的32位整数最大只能表示2GB左右的文件。超过这个大小会导致溢出或错误。但在现代的64位系统和Python 3中st_size通常是64位整数Python的int类型是任意精度的所以理论上可以处理任意大小的文件。但是需要注意一个潜在问题某些旧的或嵌入式文件系统如FAT32本身有文件大小限制如4GB。当文件达到这个限制时操作系统会阻止它继续增长所以stat调用本身不会出错但你无法创建或写入超过该限制的文件。实践建议对于处理潜在的大文件如视频、数据库、科学数据集你的代码逻辑应该能处理很大的int值。在进行单位转换时如字节转GB注意使用浮点数除法以避免整数除法截断。size_bytes some_file.stat().st_size # 正确的单位转换 size_gb size_bytes / (1024 ** 3) # 使用浮点数除法 print(f“Size: {size_gb:.2f} GB”) # 如果需要整数GB向下取整 size_gb_int size_bytes // (1024 ** 3) print(f“Size: {size_gb_int} GB (approx)”)4.4 跨平台兼容性考量虽然Python尽力提供一致的API但不同操作系统主要是Windows和Unix-like系统在文件系统细节上仍有差异。路径分隔符这是pathlib解决得最好的问题。使用Path对象和/操作符可以完全忽略这个差异。st_ctime的含义在Unix/Linux/macOS上st_ctime表示“inode变更时间”包括权限、所有者等元数据的修改。在Windows上st_ctime表示“创建时间”。因此如果你写的代码需要依赖st_ctime并在多平台运行务必明确你的需求并考虑使用st_mtime修改时间作为跨平台的标准或者根据平台做条件判断。文件权限st_modeWindows的权限模型与Unix不同。在Windows上os.chmod()的功能有限stat返回的权限位可能也不像Unix那样丰富。如果你的代码涉及复杂的权限判断需要为Windows编写备用逻辑。4.5 性能优化批量获取与缓存当需要获取成千上万个文件的大小时例如扫描整个磁盘即使是高效的stat调用累积起来也可能成为瓶颈。优化策略1使用os.scandir()(Python 3.5)os.scandir()比os.listdir()更快因为它可以在一次系统调用中返回更多的文件信息在某些操作系统上包括大小避免了为每个文件单独调用stat。import os def get_total_size_fast(directory): total_size 0 with os.scandir(directory) as it: for entry in it: if entry.is_file(): # entry.stat() 在这里可能更快因为部分信息已预取 total_size entry.stat().st_size elif entry.is_dir(): # 递归处理子目录注意对于软链接is_dir()会跟随链接 total_size get_total_size_fast(entry.path) return total_size优化策略2缓存stat结果如果文件大小不常变化而你频繁需要读取可以考虑缓存结果。from functools import lru_cache from pathlib import Path import time lru_cache(maxsize1024) def get_cached_size(path: Path) - int: “””带缓存的获取文件大小适用于文件不常变的场景。“”” # 可以在这里加入更复杂的逻辑比如检查文件是否被修改过 # 如果文件被修改了应该使缓存失效这里简化处理 return path.stat().st_size # 使用 p Path(‘config.json’) size1 get_cached_size(p) # 第一次调用会执行 stat time.sleep(1) size2 get_cached_size(p) # 第二次调用直接返回缓存结果极快 print(size1 size2) # True注意缓存策略需要根据文件的实际变化频率来设计。对于频繁写入的日志文件缓存就没什么用甚至会导致数据不一致。4.6 一个综合案例实现一个健壮的文件大小获取工具最后我们把所有知识点串起来写一个考虑相对周全的实用函数。import os from pathlib import Path from typing import Union, Optional def get_file_size( filepath: Union[str, Path], follow_symlinks: bool True, human_readable: bool False ) - Optional[Union[int, str]]: “”” 安全、灵活地获取文件大小。 参数: filepath: 文件路径可以是字符串或Path对象。 follow_symlinks: 如果为True且路径是符号链接则返回目标文件大小 如果为False则返回链接本身的大小。 human_readable: 如果为True返回带单位B, KB, MB, GB的字符串 如果为False返回字节数整数。 返回: 成功时返回大小整数或字符串失败时返回None。 “”” path Path(filepath) if isinstance(filepath, str) else filepath try: if follow_symlinks: stat_func path.stat else: stat_func path.lstat size_in_bytes stat_func().st_size except FileNotFoundError: print(f“[错误] 路径不存在: {path}“) return None except PermissionError: print(f“[错误] 权限不足无法访问: {path}“) return None except OSError as e: print(f“[错误] 访问文件时发生系统错误 ({path}): {e}“) return None if not human_readable: return size_in_bytes else: # 转换为人类可读格式 for unit in [‘B’, ‘KB’, ‘MB’, ‘GB’, ‘TB’]: if size_in_bytes 1024.0: return f“{size_in_bytes:.2f} {unit}“ size_in_bytes / 1024.0 return f“{size_in_bytes:.2f} PB” # 理论上可能更大 # 使用示例 if __name__ ‘__main__’: # 示例1: 获取字节数 size get_file_size(‘/etc/passwd’, follow_symlinksTrue, human_readableFalse) print(f“Size in bytes: {size}“) # 示例2: 获取人类可读格式不跟随符号链接 size_str get_file_size(‘/usr/bin/python3’, follow_symlinksFalse, human_readableTrue) print(f“Size (link itself): {size_str}“) # 示例3: 处理不存在的文件 size_none get_file_size(‘/non/existent/file’) if size_none is None: print(“正确处理了错误情况。”)这个函数集成了错误处理、符号链接控制、输出格式选择并且同时兼容str和Path输入算是一个比较生产就绪的工具函数了。你可以根据实际需求继续扩展它比如加入对目录大小的递归计算、支持exclude_patterns参数等。