【问题标题】:How to get all of the immediate subdirectories in Python如何在 Python 中获取所有直接子目录
【发布时间】:2010-10-22 11:03:33
【问题描述】:

我正在尝试编写一个简单的 Python 脚本,它将 index.tpl 复制到所有子目录中的 index.html 中(有一些例外)。

试图获取子目录列表让我陷入困境。

【问题讨论】:

标签: python file


【解决方案1】:
import os
def get_immediate_subdirectories(a_dir):
    return [name for name in os.listdir(a_dir)
            if os.path.isdir(os.path.join(a_dir, name))]

【讨论】:

    【解决方案2】:

    我对各种函数进行了一些速度测试,以返回所有当前子目录的完整路径

    tl;博士: 始终使用scandir

    list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]

    奖励:使用scandir,您还可以只使用f.name 而不是f.path 来获取文件夹名称。

    这个(以及下面的所有其他函数)不会使用自然排序。这意味着结果将像这样排序:1、10、2。要获得自然排序(1、2、10),请查看https://stackoverflow.com/a/48030307/2441026




    结果scandir 是:比 walk 快 3 倍,比 listdir(带过滤器)快 32 倍,比 Pathlib 快 35 倍,比 listdir 快 36 倍,比 glob 快 37 倍(!)。

    Scandir:           0.977
    Walk:              3.011
    Listdir (filter): 31.288
    Pathlib:          34.075
    Listdir:          35.501
    Glob:             36.277
    

    使用 W7x64、Python 3.8.1 测试。包含 440 个子文件夹的文件夹。
    如果您想知道 listdir 是否可以通过不执行两次 os.path.join() 来加速,是的,但基本上不存在差异。

    代码:

    import os
    import pathlib
    import timeit
    import glob
    
    path = r"<example_path>"
    
    
    
    def a():
        list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
        # print(len(list_subfolders_with_paths))
    
    
    def b():
        list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
        # print(len(list_subfolders_with_paths))
    
    
    def c():
        list_subfolders_with_paths = []
        for root, dirs, files in os.walk(path):
            for dir in dirs:
                list_subfolders_with_paths.append( os.path.join(root, dir) )
            break
        # print(len(list_subfolders_with_paths))
    
    
    def d():
        list_subfolders_with_paths = glob.glob(path + '/*/')
        # print(len(list_subfolders_with_paths))
    
    
    def e():
        list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
        # print(len(list(list_subfolders_with_paths)))
    
    
    def f():
        p = pathlib.Path(path)
        list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
        # print(len(list_subfolders_with_paths))
    
    
    
    print(f"Scandir:          {timeit.timeit(a, number=1000):.3f}")
    print(f"Listdir:          {timeit.timeit(b, number=1000):.3f}")
    print(f"Walk:             {timeit.timeit(c, number=1000):.3f}")
    print(f"Glob:             {timeit.timeit(d, number=1000):.3f}")
    print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
    print(f"Pathlib:          {timeit.timeit(f, number=1000):.3f}")
    

    【讨论】:

      【解决方案3】:

      为什么没有人提到globglob 让您可以使用 Unix 风格的路径名扩展,对于几乎所有需要查找多个路径名的事情来说,这都是我的首选。这让它变得非常容易:

      from glob import glob
      paths = glob('*/')
      

      请注意,glob 将返回带有最后一个斜杠的目录(与 unix 一样),而大多数基于 path 的解决方案将省略最后一个斜杠。

      【讨论】:

      • 很好的解决方案,简单有效。对于那些不想要最后一个斜线的人,他可以使用这个paths = [ p.replace('/', '') for p in glob('*/') ]
      • [p[:-1] for p in paths] 简单地剪切最后一个字符可能更安全,因为该替换方法还将替换文件名中任何转义的正斜杠(不是那些常见的)。
      • 更安全的是,使用 strip('/') 删除尾部斜杠。这样可以保证您不会删除任何不是正斜杠的字符
      • 通过构造,你可以保证有一个斜杠(所以它不安全),但我认为它更具可读性。不过,您肯定想使用rstrip 而不是strip,因为后者会将任何完全限定的路径转换为相对路径。
      • 对 @ari 注释的补充,适用于 Python 新手,例如 I:strip('/') 将删除起始和尾随 '/',rstrip('/') 将仅删除尾随
      【解决方案4】:

      勾选“Getting a list of all subdirectories in the current directory”。

      这是 Python 3 版本:

      import os
      
      dir_list = next(os.walk('.'))[1]
      
      print(dir_list)
      

      【讨论】:

      • 非常聪明。虽然效率并不重要(...它完全是),但我很好奇这个或基于 glob 的生成器表达式 (s.rstrip("/") for s in glob(parent_dir+"*/")) 更省时。我的直觉怀疑是基于stat()os.walk() 解决方案应该比shell 风格的通配要快得多。可悲的是,我缺乏timeit 的意愿并真正找出答案。
      • 请注意,这将返回不带父目录名称前缀的子目录名称。
      • 正是我正在寻找的......只是没有路径的目录名称 - @Cecil 我尝试了你的方法,但无法让它工作。试过tmplist=(s.rstrip("/") for s in glob(tmp+"*/")),其中 tmp 是我的父目录。它返回&lt;generator object &lt;genexpr&gt; at 0x0000018A88EE3660&gt;。我做错了什么?
      【解决方案5】:
      import os
      

      获取目录中的(完整路径)直接子目录:

      def SubDirPath (d):
          return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
      

      获取最新(最新)子目录:

      def LatestDirectory (d):
          return max(SubDirPath(d), key=os.path.getmtime)
      

      【讨论】:

      • 要获得一个列表,只需添加list( filter(...) )
      【解决方案6】:

      os.walk 是你在这种情况下的朋友。

      直接来自文档:

      walk() 通过自上而下或自下而上遍历树来在目录树中生成文件名。对于以目录 top 为根的树中的每个目录(包括 top 本身),它会产生一个 3 元组(dirpath、dirnames、filenames)。

      【讨论】:

      • 请注意,如果您只想要一级子目录,则在第一组返回值之后退出 os.walk 迭代。
      【解决方案7】:

      这个方法很好地一次性完成。

      from glob import glob
      subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
      

      【讨论】:

        【解决方案8】:

        使用 Twisted 的 FilePath 模块:

        from twisted.python.filepath import FilePath
        
        def subdirs(pathObj):
            for subpath in pathObj.walk():
                if subpath.isdir():
                    yield subpath
        
        if __name__ == '__main__':
            for subdir in subdirs(FilePath(".")):
                print "Subdirectory:", subdir
        

        由于一些评论者询问使用 Twisted 的库有什么好处,我将在这里超越原来的问题。


        在一个分支中有some improved documentation,它解释了FilePath 的优点;你可能想读一下。

        在这个例子中更具体地说:与标准库版本不同,这个函数可以用没有导入来实现。 “subdirs”函数是完全通用的,因为它只对它的参数进行操作。为了使用标准库复制和移动文件,您需要依赖内置的“open”、“listdir”、“isdir”或“os.walk”或“shutil.copy” .也可能是“os.path.join”。更不用说您需要一个字符串传递一个参数来识别实际文件。让我们看看完整的实现,它将每个目录的“index.tpl”复制到“index.html”:

        def copyTemplates(topdir):
            for subdir in subdirs(topdir):
                tpl = subdir.child("index.tpl")
                if tpl.exists():
                    tpl.copyTo(subdir.child("index.html"))
        

        上面的“subdirs”函数可以作用于任何FilePath-like 对象。这意味着,除其他外,ZipPath 对象。不幸的是,ZipPath 目前是只读的,但它可以扩展为支持写入。

        您还可以传递自己的对象以进行测试。为了测试此处建议的使用 os.path 的 API,您必须使用导入的名称和隐式依赖项进行监控,并且通常会执行黑魔法以使您的测试正常工作。使用 FilePath,您可以执行以下操作:

        class MyFakePath:
            def child(self, name):
                "Return an appropriate child object"
        
            def walk(self):
                "Return an iterable of MyFakePath objects"
        
            def exists(self):
                "Return true or false, as appropriate to the test"
        
            def isdir(self):
                "Return true or false, as appropriate to the test"
        ...
        subdirs(MyFakePath(...))
        

        【讨论】:

        • 由于我很少接触 Twisted,我总是欢迎提供更多信息和示例;很高兴看到这个答案。话虽如此,由于这种方法似乎比使用内置 python 模块和 Twisted 安装需要更多的工作,所以使用它有什么好处可以添加到答案中吗?
        • Glyph 的回答可能受到 TwistedLore 也使用 .tpl 文件这一事实的启发。
        • 好吧,显然我不指望西班牙宗教裁判所 :-) 我认为“*.tpl”是对一些抽象扩展名的通用引用,意思是“模板”,而不是特定的 Twisted 模板(我毕竟已经看到 .tpl 在许多语言中使用)。很高兴知道。
        • +1 因此用于调整到可能的 Twisted 角度,尽管我仍然想了解 Twisted 的“FilePath”对象和“walk()”函数添加到标准 API 中的内容。跨度>
        • 就我个人而言,我发现“FilePath.walk() 产生路径对象”比“os.walk 产生 dir、dirs、files 的 3 元组”更容易记住。但还有其他好处。 FilePath 允许多态性,这意味着您可以遍历文件系统以外的东西。例如,您可以将 twisted.python.zippath.ZipArchive 传递给我的“subdirs”函数,并获取 ZipPaths 的生成器而不是 FilePaths;您的逻辑不会改变,但您的应用程序现在可以神奇地处理 zip 文件。如果你想测试它,你只需要提供一个对象,你不必编写真实的文件。
        【解决方案9】:

        我刚刚写了一些代码来移动vmware虚拟机,最后使用os.pathshutil来完成子目录之间的文件复制。

        def copy_client_files (file_src, file_dst):
            for file in os.listdir(file_src):
                    print "Copying file: %s" % file
                    shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))
        

        它不是非常优雅,但确实有效。

        【讨论】:

          【解决方案10】:

          这是一种方法:

          import os
          import shutil
          
          def copy_over(path, from_name, to_name):
            for path, dirname, fnames in os.walk(path):
              for fname in fnames:
                if fname == from_name:
                  shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))
          
          
          copy_over('.', 'index.tpl', 'index.html')
          

          【讨论】:

          • -1: 不起作用,因为 shutil.copy 将复制到当前目录,所以你最终会为每个 'index.tpl' 覆盖当前目录中的 'index.html' 一次' 您可以在子目录树中找到。
          【解决方案11】:

          不得不提path.py 库,我经常使用它。

          获取直接子目录就这么简单:

          my_dir.dirs()

          完整的工作示例是:

          from path import Path
          
          my_directory = Path("path/to/my/directory")
          
          subdirs = my_directory.dirs()
          

          注意:my_directory 仍然可以作为字符串进行操作,因为 Path 是字符串的子类,但提供了许多有用的方法来操作路径

          【讨论】:

            【解决方案12】:
            def get_folders_in_directories_recursively(directory, index=0):
                folder_list = list()
                parent_directory = directory
            
                for path, subdirs, _ in os.walk(directory):
                    if not index:
                        for sdirs in subdirs:
                            folder_path = "{}/{}".format(path, sdirs)
                            folder_list.append(folder_path)
                    elif path[len(parent_directory):].count('/') + 1 == index:
                        for sdirs in subdirs:
                            folder_path = "{}/{}".format(path, sdirs)
                            folder_list.append(folder_path)
            
                return folder_list
            

            下面的函数可以调用为:

            get_folders_in_directories_recursively(directory, index=1) -> 给出第一级的文件夹列表

            get_folders_in_directories_recursively(directory) -> 给出所有子文件夹

            【讨论】:

            • 表现不错,python 3.6 版本,但我需要从函数变量内部删除“self”
            【解决方案13】:
            import glob
            import os
            
            def child_dirs(path):
                 cd = os.getcwd()        # save the current working directory
                 os.chdir(path)          # change directory 
                 dirs = glob.glob("*/")  # get all the subdirectories
                 os.chdir(cd)            # change directory to the script original location
                 return dirs
            

            child_dirs 函数接受一个目录路径并返回其中的直接子目录的列表。

            dir
             |
              -- dir_1
              -- dir_2
            
            child_dirs('dir') -> ['dir_1', 'dir_2']
            

            【讨论】:

              【解决方案14】:
              import pathlib
              
              
              def list_dir(dir):
                  path = pathlib.Path(dir)
                  dir = []
                  try:
                      for item in path.iterdir():
                          if item.is_dir():
                              dir.append(item)
                      return dir
                  except FileNotFoundError:
                      print('Invalid directory')
              

              【讨论】:

                【解决方案15】:

                一个使用 pathlib 的班轮:

                list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
                

                【讨论】:

                  猜你喜欢
                  • 2022-06-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-08-14
                  • 1970-01-01
                  • 2011-02-04
                  • 2012-07-22
                  • 2014-01-07
                  • 2011-12-08
                  相关资源
                  最近更新 更多