【问题标题】:very quickly getting total size of folder很快获得文件夹的总大小
【发布时间】:2011-01-29 22:52:55
【问题描述】:

我想使用 python 快速找到任何文件夹的总大小。

import os
from os.path import join, getsize, isfile, isdir, splitext
def GetFolderSize(path):
    TotalSize = 0
    for item in os.walk(path):
        for file in item[2]:
            try:
                TotalSize = TotalSize + getsize(join(item[0], file))
            except:
                print("error with file:  " + join(item[0], file))
    return TotalSize

print(float(GetFolderSize("C:\\")) /1024 /1024 /1024)

这是我为获取文件夹总大小而编写的简单脚本,大约需要 60 秒(+-5 秒)。通过使用多处理,我在四核机器上将处理时间缩短到了 23 秒。

使用 Windows 文件资源管理器只需约 3 秒(右键单击-> 属性自行查看)。那么有没有一种更快的方法来找到接近 windows 可以做到的文件夹的总大小?

Windows 7,python 2.6(进行了搜索,但大多数时候人们使用的方法与我自己的方法非常相似) 提前致谢。

【问题讨论】:

  • 提供的代码无效。您能否发布一个您实际运行过的完整的最小示例?
  • 不好意思,之前只有这个功能,剩下的都编辑进去了。

标签: python optimization directory


【解决方案1】:

你处于劣势。

Windows 资源管理器几乎肯定会使用FindFirstFile/FindNextFile 来遍历目录结构一次性收集大小信息(通过lpFindFileData),这实际上是一个系统调用每个文件。

不幸的是,在这种情况下,Python 不是你的朋友。因此,

  1. os.walk 首先调用os.listdir(内部调用FindFirstFile/FindNextFile
    • 从此时起进行的任何额外系统调用只会让您比 Windows 资源管理器慢
  2. os.walk 然后为 os.listdir 返回的每个文件调用 isdir(内部调用 GetFileAttributesEx -- 或者,在 Win2k 之前,GetFileAttributes+FindFirstFile 组合)重新确定是否递归
  3. os.walkos.listdir 将执行额外的内存分配、字符串和数组操作等来填写它们的返回值
  4. 然后为os.walk返回的每个文件调用getsize再次调用GetFileAttributesEx

每个文件的系统调用量是 Windows 资源管理器的 3 倍,加上内存分配和操作开销。

您可以使用Anurag的解决方案,也可以尝试直接递归调用FindFirstFile/FindNextFile(这应该与cygwin或其他win32 portdu -s some_directory的性能相当。)

os.walk的实现参考os.pylistdirwin32_stat的实现参考posixmodule.cisdirgetsize都调用。)

请注意,Python 的 os.walk 在所有平台上都不是最佳的(Windows 和 *nices),包括 Python3.1。在 Windows 和 *nices 上,os.walk 可以在不调用 isdir 的情况下一次性实现遍历,因为 FindFirst/FindNext (Windows) 和 opendir/readdir (*nix) 已经通过返回文件类型lpFindFileData->dwFileAttributes (Windows) 和 dirent::d_type (*nix)。

也许违反直觉,在大多数现代配置(例如 Win7 和 NTFS,甚至一些 SMB 实现)上,GetFileAttributesEx 的速度比单个文件的 FindFirstFile两倍(甚至可能比迭代更慢在带有FindNextFile 的目录上。)

更新: Python 3.5 包含新的PEP 471 os.scandir() 函数,该函数通过返回文件属性和文件名来解决此问题。这个新功能用于加速内置的os.walk()(在 Windows 和 Linux 上)。您可以使用 scandir module on PyPI 为较旧的 Python 版本(包括 2.x)获取此行为。

【讨论】:

【解决方案2】:

如果您想要与资源管理器相同的速度,为什么不使用 windows 脚本来访问使用 pythoncom 的相同功能,例如

import win32com.client as com

folderPath = r"D:\Software\Downloads"
fso = com.Dispatch("Scripting.FileSystemObject")
folder = fso.GetFolder(folderPath)
MB = 1024 * 1024.0
print("%.2f MB" % (folder.Size / MB))

它将与资源管理器一样工作,您可以在http://msdn.microsoft.com/en-us/library/bstcxhf7(VS.85).aspx 阅读更多关于脚本运行时的信息。

【讨论】:

  • 效果很好,实际上很神奇。但只是大多数时候。在一个大小为 37GB 和 7 000 个文件的目录('C:\Downloads')中,您的方法几乎可以立即获得结果。 os.walk() 方法会在几秒钟(3 秒)内返回结果,但我在其他目录(例如 C:\Windows、C:\users 等)上遇到了一些问题,它说发生了异常。跨度>
  • @freakazo,C:\Windows 在我的机器上工作,你得到什么错误?
  • 回溯(最近一次调用最后):文件“Test.py”,第 7 行,在 中打印“%.2f MB”%(folder.Size/MB) 文件“C:\ python26_32\lib\site-packages\win32com\client\dynamic.py",第 501 行,在 getattr 中 ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1) pywintypes.com_error : (-2147352567, '发生异常。', (0, None, None, None, 0, -2146828218), None) 按任意键继续。 . . ### 更多测试表明是文件夹大小导致了问题。 folder.name 例如适用于 C:\Windows 目录
  • 这当然早就死了,但我在 c:\users\myname 上得到了同样的东西并查找它,这是权限被拒绝。它适用于您自己创建的任何东西,但任何系统性的东西似乎都行不通,即使以管理员身份运行脚本也是如此。
【解决方案3】:

我将 Python 代码的性能与包含 190k 文件的 15k 目录树进行了比较,并将其与可能与操作系统一样快的 du(1) 命令进行了比较。 Python 代码耗时 3.3 秒,而 du 耗时 0.8 秒。这是在 Linux 上。

我不确定 Python 代码中是否有很多内容。还要注意 du 的第一次运行需要 45 秒,这显然是在相关 i 节点进入块缓存之前;因此,这种性能在很大程度上取决于系统管理其商店的能力。如果其中一个或两个都不会让我感到惊讶:

  1. os.path.getsize 在 Windows 上不是最理想的
  2. Windows 缓存计算后的目录内容大小

【讨论】:

  • 在 Windows 上看起来确实比较慢,在具有 23K 目录树和 175K 文件的 Windows 上大约需要 60 秒。使用 du windows 等效项需要 6 秒才能完成。所以看起来 Python 在 windows 上比 du 慢 10 倍,在 linux 上慢 4 倍。所以 yip 似乎 1. os.path.getsize/os.walk 在 Windows 2 上确实是次优的。Windows 似乎缓存了大小为 3 的目录内容。Windows 仍然比 linux 慢
猜你喜欢
  • 2021-01-01
  • 2014-03-01
  • 2023-04-11
  • 2021-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-09
  • 1970-01-01
相关资源
最近更新 更多