【问题标题】:Using tqdm on a for loop inside a function to check progress在函数内的 for 循环上使用 tqdm 来检查进度
【发布时间】:2016-03-13 11:04:11
【问题描述】:

我正在使用 for 循环遍历目录树中的大型组文件。

这样做时,我想通过控制台中的进度条监控进度。因此,我决定为此使用 tqdm。

目前,我的代码如下所示:

for dirPath, subdirList, fileList in tqdm(os.walk(target_dir)):
        sleep(0.01)
        dirName = dirPath.split(os.path.sep)[-1]
        for fname in fileList:
        *****

输出:

Scanning Directory....
43it [00:23, 11.24 it/s]

所以,我的问题是它没有显示进度条。我想知道如何正确使用它并更好地了解它的工作原理。另外,如果这里还有其他可以使用的 tqdm 替代品。

【问题讨论】:

    标签: python python-2.7 progress tqdm


    【解决方案1】:

    除非您知道“完成”是什么意思,否则您无法显示完成百分比

    虽然os.walk 正在运行,但它不知道最终要迭代多少文件和文件夹:os.walk 的返回类型没有__len__。它必须一直向下查看目录树,枚举所有文件和文件夹,以便计算它们。换句话说,os.walk 必须完成所有工作两次才能告诉您它将生产多少物品,这是低效的。

    如果您不喜欢显示进度条,您可以将数据假脱机到内存列表中:list(os.walk(target_dir))。我不推荐这个。如果您正在遍历大型目录树,这可能会消耗大量内存。更糟糕的是,如果 followlinksTrue 并且你有一个循环目录结构(孩子链接到他们的父母),那么它可能会永远循环下去,直到你的 RAM 用完。

    【讨论】:

      【解决方案2】:

      作为explained in the documentation,这是因为您需要提供进度指示器。根据您对文件的处理方式,您可以使用文件计数或文件大小。

      其他答案建议将os.walk() 生成器转换为列表,以便获得__len__ 属性。但是,这将花费您大量的内存,具体取决于您拥有的文件总数。

      另一种可能性是预计算:您首先遍历整个文件树并计算文件总数(但不保留文件列表,只需计数!),然后您可以遍历再次向tqdm 提供您预先计算的文件数:

      def walkdir(folder):
          """Walk through every files in a directory"""
          for dirpath, dirs, files in os.walk(folder):
              for filename in files:
                  yield os.path.abspath(os.path.join(dirpath, filename))
      
      # Precomputing files count
      filescount = 0
      for _ in tqdm(walkdir(target_dir)):
          filescount += 1
      
      # Computing for real
      for filepath in tqdm(walkdir(target_dir), total=filescount):
              sleep(0.01)
              # etc...
      

      请注意,我在os.walkdir 上定义了一个包装函数:因为您正在处理文件而不是目录,所以最好定义一个处理文件而不是目录的函数。

      但是,不使用 walkdir 包装器也可以获得相同的结果,但它会有点复杂,因为您必须在遍历每个子文件夹后恢复上一个进度条状态:

      # Precomputing
      filescount = 0
      for dirPath, subdirList, fileList in tqdm(os.walk(target_dir)):
          filescount += len(filesList)
      
      # Computing for real
      last_state = 0
      for dirPath, subdirList, fileList in os.walk(target_dir):
          sleep(0.01)
          dirName = dirPath.split(os.path.sep)[-1]
          for fname in tqdm(fileList, total=filescount, initial=last_state):
              # do whatever you want here...
          # Update last state to resume the progress bar
          last_state += len(fileList)
      

      【讨论】:

        【解决方案3】:

        这是因为tqdm 不知道os.walk 的结果会持续多久,因为它是一个生成器,所以len 不能被调用。您可以通过先将os.walk(target_dir) 转换为列表来解决此问题:

        for dirPath, subdirList, fileList in tqdm(list(os.walk(target_dir))):
        

        来自tdqm 模块的文档:

        如果可能,使用

        len(iterable)。作为最后的手段,只有基本的 显示进度统计信息(没有 ETA,没有进度条)。

        但是,len(os.walk(target_dir)) 是不可能的,所以没有 ETA 或进度条。

        正如 Benjamin 指出的那样,使用 list 确实会使用一些内存,但不会太多。大约 190,000 个文件的假脱机目录导致 Python 在我的 Windows 10 机器上使用此代码使用大约 65MB 的内存。

        【讨论】:

        • 65MB 是相当多的内存!如果你的目录结构是循环的,那么它最终会使用无限的内存,甚至超过 65MB。
        【解决方案4】:

        这是一种更简洁的方法,可以预先计算文件数量,然后在文件上提供状态栏:

        file_count = sum(len(files) for _, _, files in os.walk(folder))  # Get the number of files
        with tqdm(total=file_count) as pbar:  # Do tqdm this way
            for root, dirs, files in os.walk(folder):  # Walk the directory
                for name in files:
                    pbar.update(1)  # Increment the progress bar
                    # Process the file in the walk
        

        【讨论】:

        • 使用生成器来计算文件的数量非常简洁!另外,这一定是首选答案!
        【解决方案5】:

        您可以通过这种方式使用tqdm 对目录路径内的所有文件进行处理。

        from tqdm import tqdm
        target_dir = os.path.join(os.getcwd(), "..Your path name")#it has 212 files
        for r, d, f in os.walk(target_dir):
            for file in tqdm(f, total=len(f)):
                filepath = os.path.join(r, file)
                #f'Your operation on file..{filepath}'
        

        20%|████████████████████ | 42/212 [05:07

        这样你会有进步的……

        【讨论】:

          【解决方案6】:

          这是我对类似问题的解决方案:

              for root, dirs, files in os.walk(local_path):
                  path, dirs, files = os.walk(local_path).next()
                  count_files = (int(len(files)))
                  for i in tqdm.tqdm(range(count_files)):
                      time.sleep(0.1)
                      for fname in files:
                          full_fname = os.path.join(root, fname)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-01-30
            • 2018-10-12
            • 2020-09-24
            • 2018-06-30
            • 2021-02-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多