您好我有以下程序,
问题: - 如何使它更优雅,更可读,更紧凑。 - 我该怎么做才能将常见循环提取到另一个方法。
假设:
从给定的rootDir开始,dirs的组织方式如下所示。
proc的作用:
如果输入为200,则删除所有OLDER超过200天的DIRS。不是基于修改时间,而是基于dir结构和dir名称[我稍后会在每个老版本的dd上用暴力“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?
答案 0 :(得分:1)
这实际上看起来很不错,除了这篇评论中提到的一件大事:
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”,您还可以找到其他方法来实现初始拆分 - 但请记住,大多数人都要关注能够对任意迭代进行分区(这里不需要)或者,无论是否正确,都担心效率(这里你也不关心)。所以,不要寻找有人说“最好”的答案;看看所有的答案,然后选择一个看起来最具可读性的答案。
最后,你可能会注意到你最终得到的三个级别几乎相同:
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版本,那么早期的东西几乎是微不足道的。写入的递归版本将更加丑陋且更加痛苦。但是在处理递归生成器时实际上没有办法避免这种情况,因为子生成器不能“通过”调用生成器。
答案 1 :(得分:1)
我建议不要使用发电机,除非你绝对确定需要它们。在这种情况下,您不需要它们。
在下面,并不严格需要newer_list
。虽然categorizeSubdirs
可以递归,但我不认为复杂性的增加值得重复节省(但这只是个人风格问题;当不清楚需要多少级别的递归时,我只使用递归或数字固定但很大;三个不够IMO。
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
最终的三个嵌套循环可以减少重复性,代价是代码复杂性。我不认为这是值得的,尽管合理的人可以不同意。在其他条件相同的情况下,我更喜欢更简单的代码,而不是更聪明的代码;正如他们所说,阅读代码比编写代码更难,所以如果你能编写最聪明的代码,那么你就不会聪明地阅读它。 :/