python循环优化迭代dirs 3级和d

2024-04-18 00:54:10 发布

您现在位置:Python中文网/ 问答频道 /正文

嗨,我有以下程序

问题: -如何使其美观、可读性更强、简洁。 -如何将公共循环提取到另一个方法。你知道吗

假设:

在给定的rootDir中,dir的组织方式如下所示。你知道吗

过程的作用:

如果输入是200,它将删除所有超过200天的目录。不是基于modifytime,而是基于dir结构和dir名称[我稍后将对每个较旧的dir强制删除“rm-Rf”]

例如dir结构:

-2009(year dirs) [will force delete dirs e.g "rm -Rf" later]
-2010
  -01...(month dirs)
  -05 ..
      -01.. (day dirs)
         -many files. [I won't check mtime at file level - takes more time]
      -31
  -12
-2011
-2012 ...

我拥有的代码:

def get_dirs_to_remove(dir_path, olderThanDays):
    today = datetime.datetime.now();
    oldestDayToKeep = today + datetime.timedelta(days= -olderThanDays) 
    oldKeepYear = int(oldestDayToKeep.year)
    oldKeepMonth =int(oldestDayToKeep.month);
    oldKeepDay = int(oldestDayToKeep.day);
    for yearDir in os.listdir(dirRoot):
        #iterate year dir
        yrPath = os.path.join(dirRoot, yearDir);
        if(is_int(yearDir) == False):
            problemList.append(yrPath); # can't convery year to an int, store and report later 
            continue

        if(int(yearDir) < oldKeepYear):
                print "old Yr dir: " + yrPath
                #deleteList.append(yrPath); # to be bruteforce deleted e.g "rm -Rf"
                yield yrPath;
                continue
        elif(int(yearDir) == oldKeepYear):
            # iterate month dir
            print "process Yr dir: " + yrPath
            for monthDir in os.listdir(yrPath):
                monthPath = os.path.join(yrPath, monthDir)
                if(is_int(monthDir) == False):
                    problemList.append(monthPath);
                    continue
                if(int(monthDir) < oldKeepMonth):
                        print "old month dir: " + monthPath
                        #deleteList.append(monthPath);
                        yield monthPath;
                        continue
                elif (int(monthDir) == oldKeepMonth):
                    # iterate Day dir
                    print "process Month dir: " + monthPath
                    for dayDir in os.listdir(monthPath):
                        dayPath = os.path.join(monthPath, dayDir)
                        if(is_int(dayDir) == False):
                            problemList.append(dayPath);
                            continue
                        if(int(dayDir) < oldKeepDay):
                            print "old day dir: " + dayPath
                            #deleteList.append(dayPath);
                            yield dayPath
                            continue
print [ x for x in get_dirs_to_remove(dirRoot, olderThanDays)]
print "probList" %  problemList # how can I get this list also from the same proc?

Tags: ifosdiryearintprintdirscontinue
2条回答

这看起来很不错,除了这条评论中提到的一件大事:

print "probList" %  problemList # how can I get this list also from the same proc?

听起来像是将problemList存储在一个全局变量或其他什么东西中,您想解决这个问题。以下是一些方法:

  • 同时产生删除文件和问题文件—例如,产生一个tuple,其中第一个成员说明它是哪种类型的,第二个成员说明如何处理它。你知道吗
  • problemList作为参数。请记住list是可变的,因此附加到参数的内容对调用方是可见的。你知道吗
  • yield结尾的problemList,这意味着您需要重新构造使用生成器的方式,因为它不再只是一个简单的迭代器。你知道吗
  • 将生成器编码为类而不是函数,并将problemList存储为成员变量。你知道吗
  • 查看内部生成器信息并将problemList塞进其中,以便调用者可以检索它。你知道吗

同时,有几种方法可以使代码更加紧凑和可读。你知道吗

最琐碎的是:

print [ x for x in get_dirs_to_remove(dirRoot, olderThanDays)]

此列表理解与原始迭代完全相同,您可以更简单地编写为:

print list(get_dirs_to_remove(dirRoot, olderThanDays))

至于算法本身,您可以对listdir进行分区,然后只使用分区的list即可

yearDirs = os.listdir(dirRoot):
problemList.extend(yearDir for yearDir in yearDirs if not is_int(yearDir))
yield from (yearDir for yearDir in yearDirs if int(yearDir) < oldKeepYear)
for year in (yearDir for yearDir in yearDirs if int(yearDir) == oldKeepYear):
    # next level down

或者严格地说:

yearDirs = os.listdir(dirRoot)
problems, older, eq, newer = partitionDirs(yearDirs, oldKeepYear)
problemList.extend(problems)
yield from older
for year in eq:
    # next level down

后者可能更有意义,特别是考虑到yearDirs已经是一个列表了,而且不太可能有那么大。你知道吗

当然,您需要编写partitionDirs函数,但好在,您可以在月份和日期级别再次使用它。这很简单。事实上,我实际上可能通过排序来进行分区,因为它使逻辑变得如此明显,即使它更加冗长:

def partitionDirs(dirs, keyvalue):
    problems = [dir for dir in dirs if not is_int(dir)]
    values = sorted(dir for dir in dirs if is_int(dir), key=int)
    older, eq, newer = partitionSortedListAt(values, keyvalue, key=int)

如果你环顾四周(也许搜索“python分区排序列表”?),您可以找到很多方法来实现partitionSortedListAt函数,但下面是一些我认为对于没有这样考虑问题的人来说很容易理解的东西:

    i = bisect.bisect_right(vals, keyvalue)
    if vals[i] == keyvalue:
        return problems, vals[:i], [vals[i]], vals[i+1:]
    else:
        return problems, vals[:i], [], vals[i:]

如果您搜索“python split predicate”,您还可以找到实现初始剥离的其他方法,尽管请记住,大多数人要么关心能够划分任意iterables(这里不需要),要么关心效率(这里也不关心)。所以,不要寻找别人说的“最好”的答案;看看所有的答案,选一个你觉得最容易理解的。你知道吗

最后,您可能会注意到,您最终得到了三个看起来几乎相同的级别:

yearDirs = os.listdir(dirRoot)
problems, older, eq, newer = partitionDirs(yearDirs, oldKeepYear)
problemList.extend(problems)
yield from older
for year in eq:
    monthDirs = os.listdir(os.path.join(dirRoot, str(year)))
    problems, older, eq, newer = partitionDirs(monthDirs, oldKeepMonth)
    problemList.extend(problems)
    yield from older
    for month in eq:
        dayDirs = os.listdir(os.path.join(dirRoot, str(year), str(month)))
        problems, older, eq, newer = partitionDirs(dayDirs, oldKeepDay)
        problemList.extend(problems)
        yield from older
        yield from eq

您可以通过递归进一步简化这一点,传递到目前为止的路径,以及要检查的进一步级别的列表,您可以将这18行转换为9行。这是否更具可读性取决于您对要传递的信息进行编码的程度以及适当的yield from。下面是想法的草图:

def doLevel(pathSoFar, dateComponentsLeft):
    if not dateComponentsLeft:
        return
    dirs = os.listdir(pathSoFar)
    problems, older, eq, newer = partitionDirs(dirs, dateComponentsLeft[0])
    problemList.extend(problems)
    yield from older
    if eq:
        yield from doLevel(os.path.join(pathSoFar, eq[0]), dateComponentsLeft[1:]))
yield from doLevel(rootPath, [oldKeepYear, oldKeepMonth, oldKeepDay])

如果您使用的是一个没有yield from的老Python版本,那么早期的东西几乎不需要转换;编写的递归版本将更难看、更痛苦。但在处理递归生成器时,确实无法避免这种情况,因为子生成器不能“通过”调用生成器。你知道吗

我建议不要使用发电机,除非你绝对确定你需要它们。在这种情况下,你不需要它们。你知道吗

在下面的例子中,newer_list不是严格需要的。虽然categorizeSubdirs可以进行递归,但我觉得复杂性的增加并不值得重复性的节省(但这只是个人风格的问题;我只在不清楚需要多少级递归或者数字是固定的但很大时才使用递归;在我看来,三级是不够的)。你知道吗


def categorizeSubdirs(keep_int, base_path):
    older_list = []
    equal_list = []
    newer_list = []
    problem_list = []

    for subdir_str in os.listdir(base_path):
        subdir_path = os.path.join(base_path, subdir_str))
        try:
            subdir_int = int(subdir_path)
        except ValueError:
            problem_list.append(subdir_path)
        else:
            if subdir_int  keep_int:
                newer_list.append(subdir_path)
            else:
                equal_list.append(subdir_path)

    # Note that for your case, you don't need newer_list, 
    # and it's not clear if you need problem_list
    return older_list, equal_list, newer_list, problem_list

def get_dirs_to_remove(dir_path, olderThanDays):
    oldest_dt = datetime.datetime.now() datetime.timedelta(days= -olderThanDays) 
    remove_list = []
    problem_list = []

    olderYear_list, equalYear_list, newerYear_list, problemYear_list = categorizeSubdirs(oldest_dt.year, dir_path))
    remove_list.extend(olderYear_list)
    problem_list.extend(problemYear_list)

    for equalYear_path in equalYear_list:
        olderMonth_list, equalMonth_list, newerMonth_list, problemMonth_list = categorizeSubdirs(oldest_dt.month, equalYear_path))
        remove_list.extend(olderMonth_list)
        problem_list.extend(problemMonth_list)

        for equalMonth_path in equalMonth_list:
            olderDay_list, equalDay_list, newerDay_list, problemDay_list = categorizeSubdirs(oldest_dt.day, equalMonth_path))
            remove_list.extend(olderDay_list)
            problem_list.extend(problemDay_list)

    return remove_list, problem_list

最后的三个嵌套循环可以减少重复,但代价是代码复杂度。我认为这不值得,尽管理智的人可能不同意。在其他条件相同的情况下,我更喜欢简单的代码,而不是稍微聪明一点的代码;正如他们所说,读代码比写代码更难,所以如果你写了你能写的最聪明的代码,你就不会足够聪明去读它。:/

相关问题 更多 >

    热门问题