1

こんにちは、次の手順があります。

質問: - エレガントで、読みやすく、コンパクトにする方法。- 共通ループを別のメソッドに抽出するにはどうすればよいですか?

仮定:

特定の rootDir から、ディレクトリは以下の例のように編成されます。

proc の機能:

入力が 200 の場合、200 日より古いすべての DIRS が削除されます。変更時間に基づくのではなく、ディレクトリ構造とディレクトリ名に基づいています[後で古いディレクトリごとに力ずくで「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?
4

2 に答える 2

1

これは、このコメントで言及されている1つの大きなことを除いて、実際にはかなりいいように見えます:

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

グローバル変数か何かに保存しているように聞こえますがproblemList、それを修正したいと考えています。これを行うには、いくつかの方法があります。

  • 削除ファイルと問題ファイルの両方を生成します。たとえば、tuple最初のメンバーがそれがどの種類であるかを示し、2 番目のメンバーがそれをどうするかを示します。
  • problemListをパラメーターとして受け取ります。slistは変更可能であるため、引数への追加は呼び出し元に表示されることに注意してください。
  • yieldつまりproblemList、ジェネレーターは単純な反復子ではなくなったため、ジェネレーターの使用方法を再構築する必要があります。
  • ジェネレーターを関数ではなくクラスとしてコーディングしproblemList、メンバー変数として格納します。
  • 発信者が取得できるように、内部ジェネレーター情報をproblemList確認してそこに詰め込みます。

それまでの間、コードをよりコンパクトで読みやすくする方法がいくつかあります。

最も些細なこと:

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

このリスト内包表記は元の反復とまったく同じで、次のように簡単に記述できます。

print list(get_dirs_to_remove(dirRoot, olderThanDays))

アルゴリズム自体については、 を分割してlistdirから、分割された s を使用することができます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 partition sorted list」で検索してみては?)、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」を検索すると、最初の分割を実装する他の方法も見つけることができますが、ほとんどの人は任意のイテラブル (ここでは必要ありません) を分割できることに関心があることに注意してください。当然かどうかにかかわらず、効率が心配です(ここでも気にしません)。したがって、誰かが「最高」と言う答えを探してはいけません。すべての回答を見て、最も読みやすいと思われるものを選んでください。

最後に、ほとんど同じに見える 3 つのレベルになってしまうことに気付くかもしれません。

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])

を持たない古いバージョンの Python を使用しているyield from場合、以前のものを変換するのはほとんど簡単です。書かれている再帰バージョンは、より醜く、より苦痛になります。しかし、サブジェネレーターは呼び出し元のジェネレーターを "yield through" できないため、再帰的なジェネレーターを扱うときにこれを回避する方法は実際にはありません。

于 2012-12-20T21:33:48.487 に答える
1

ジェネレーターが必要であることが確実でない限り、ジェネレーターを使用しないことをお勧めします。この場合、それらは必要ありません。

以下でnewer_listは、厳密には必要ありません。再帰的にcategorizeSubdirsすることもできますが、複雑さの増加が繰り返しの節約に値するとは思いません (しかし、それは個人的なスタイルの問題です。再帰のレベルがいくつ必要か、または数が固定されているかが不明な場合にのみ再帰を使用しますが、大きい; IMO では 3 つでは不十分です)。


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

最後の 3 つのネストされたループは、コードの複雑さを犠牲にして、反復を少なくすることができます。合理的な人々は反対するかもしれませんが、それは価値があるとは思いません。他のすべてが同じであれば、私は少し巧妙なコードよりも単純なコードを好みます。彼らが言うように、コードを読むことはそれを書くことよりも難しいので、できる限り賢いコードを書いたとしても、それを読むほど賢くはなりません。:/

于 2012-12-21T07:57:35.723 に答える