Interpreterパターン (2) ファイル検索用言語
『Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。目次
Interpreterパターンで簡単なファイル検索言語を作ります。
パーツとしては、
- All
- 全てのファイルを返す
- FileName
- パターンにマッチするファイルを返す
- Bigger
- 指定サイズより、大きなファイルを返す
- Or
- 2つの式のどちらかに含まれるファイルを返す
- And
- 2つの式の両方に含まれるファイルを返す
- Not
- 式に含まれないファイルを返す
等を用意します。
import os import fnmatch class Expression(object): pass def allfiles(dirpath): """dirpathの下に含まれる、全てのファイルのリストを返す""" result = [] for root, dirs, files in os.walk(dirpath): for f in files: result.append(os.path.join(root, f)) return result class All(Expression): def evaluate(self, dirpath): return allfiles(dirpath) class FileName(Expression): def __init__(self, pattern): self.pattern = pattern def evaluate(self, dirpath): return fnmatch.filter(allfiles(dirpath), self.pattern) class Bigger(Expression): def __init__(self, size): self.size = size def evaluate(self, dirpath): return [path for path in allfiles(dirpath) if os.path.getsize(path) > self.size] class Not(Expression): def __init__(self, expression): self.expression = expression def evaluate(self, dirpath): result1 = set(allfiles(dirpath)) result2 = set(self.expression.evaluate(dirpath)) return list(result1 - result2) class Or(Expression): def __init__(self, expression1, expression2): self.expression1 = expression1 self.expression2 = expression2 def evaluate(self, dirpath): result1 = set(self.expression1.evaluate(dirpath)) result2 = set(self.expression2.evaluate(dirpath)) return list(result1 | result2) class And(Expression): def __init__(self, expression1, expression2): self.expression1 = expression1 self.expression2 = expression2 def evaluate(self, dirpath): result1 = set(self.expression1.evaluate(dirpath)) result2 = set(self.expression2.evaluate(dirpath)) return list(result1 & result2) if __name__ == "__main__": from pprint import pprint expr = Not(And(Bigger(5 * 1024), FileName("*.py"))) path = r"./" for p in expr.evaluate(path): print p
最後に使用例がありますが、このプログラムを実行すると、
「サイズ5kB以上の*.py ファイル」以外のファイルのリストを返します。
もちろん、その気になれば、もっと複雑な条件も作っていけます。
ちなみに、演算子オーバーロードを使えば、複雑な条件を書きやすくなります。
組み込みのset型に倣って、ビット演算子の" | "を Or、" & "を And、" ~ " を Notに使います。
class Expression(object): def __and__(self, other): return And(self, other) def __or__(self, other): return Or(self, other) def __invert__(self): return Not(self)
これで、Interpreterパターンはこれでお仕舞です。
ひとたびASTが手に入れば、式の計算なり、ファイル検索なりを行う事ができます。
問題は、どうやってASTを手に入れるかです・・・