2012-03-12 2 views
2

私は、ログファイルを数千行から数十万行の間で解析する小さなプログラムを作成しました。このために、私はすべての行を解析し、キーワードを探し、関連付けられた値を持つキーワードを返す関数をコード内に持っています。文字列の書式設定とexecを使用せずに、ダイナミック関数を作成するより良い方法はありますか?

これらのログファイルには、ほとんどセクションが含まれていません。各セクションには、私が興味を持ち、辞書として保存したいいくつかの値があります。

私は以下のサンプルを簡略化しましたが、アイデアは同じです。

私の本来の機能は、それが実行ごとに100と10000倍の間で呼び出され、このように見えたので、私はそれを最適化する理由を理解することができます

def parse_txt(f): 
    d = {} 
    for line in f: 
     if not line: 
      pass 

     elif 'apples' in line: 
      d['apples'] = True 

     elif 'bananas' in line: 
      d['bananas'] = True 

     elif line.startswith('End of section'): 
      return d 


f = open('fruit.txt','r') 
d = parse_txt(f) 
print d 

私はに実行し、問題を、私は持っているということです私のプログラムでは多くの条件があります。なぜなら、それはたくさんの異なるものをチェックし、その値を格納するからです。そして、0〜30個のキーワードの間でどこでもすべての行をチェックすると、遅くなります。私はそれをしたいとは思っていません。プログラムを実行するたびに、私はすべてに興味があるからではありません。私は5〜6個のキーワードにしか関心がありませんが、30個ほどのキーワードについてすべての行を解析しています。

それを最適化するために、私は、文字列の上のexecを使って、次のことを書いた:

def make_func(args): 
    func_str = """ 
def parse_txt(f): 
    d = {} 
    for line in f: 
     if not line: 
      pass 
    """ 

    if 'apples' in args: 
     func_str += """ 
     elif 'apples' in line: 
      d['apples'] = True 
""" 

    if 'bananas' in args: 
     func_str += """ 
     elif 'bananas' in line: 
      d['bananas'] = True 
""" 

    func_str += """ 
     elif line.startswith('End of section'): 
      return d""" 

    print func_str 
    exec(func_str) 
    return parse_txt 

args = ['apples','bananas'] 
fun = make_func(args) 
f = open('fruit.txt','r') 
d = fun(f) 
print d 

それは大きさの順でプログラムを高速化し、それが比較的簡単であるため、このソリューションは、素晴らしい作品。私が入れた議論に応じて、それは私に最初の機能を与えますが、私が必要としないすべてのものをチェックすることはありません。

たとえば、私がargs=['bananas']とした場合、それは正確に私がしたいことである'apples'をチェックしません。

これにより、効率が大幅に向上します。

しかし、私は何かを変更するたびに、非常に読みやすく、変更するのが難しく、非常にエラーが発生しやすいので、このソリューションは大変気に入りました。それ以外に、それは少し汚れているように感じます。

私はこれを行うための代替方法またはより良い方法を探しています。私は、すべての行を呼び出すために一連の関数を使用しようとしましたが、これはうまくいきましたが、私の現在の解決策が私に与えるスピードの向上はありませんでした。私の現在の解決策は、プログラムの開始時に一度だけ呼び出されなければならないので、この問題はありません。私はexecとevalのセキュリティ問題について読んだことがありますが、私はそれを使っている唯一の人なので、実際には気にしません。

編集: わかりやすくするために、私は機能を大幅に簡略化しました。答えから私はこれを十分に明確にしていないことを理解しています。 私は一貫した方法でキーワードをチェックしません。時々、私は1つの行に2つまたは3つのキーワードをチェックする必要があります。時には1の場合もあります。私は同じように結果を扱いません。たとえば、時々私は私が行っている行から単一の値を抽出します。時には次の5行を解析する必要があります。

答えて

4

私はあなたが(「キーワード」)を探したいキーワードのリストを定義し、このやってしようとするだろう:私は「ない限り

dict([(word,True) for word in keywords if word in line]) 

for word in keywords: 
    if word in line: 
     d[word] = True 

あるいは、リストの内包表記を使用してこれはあなたのバージョンよりもはるかに遅くすべきではありません。

ここではevalを使用する必要はありません、私の意見では。 evalベースのソリューションでは、ほとんどの場合赤いフラグが立てられるはずです。

編集:あなたがキーワードに応じて、異なるアクションを実行する必要があるとして、私はちょうど機能ハンドラを定義し、このように辞書を使用します。

def keyword_handler_word1(line): 
    (...) 

(...) 

def keyword_handler_wordN(line): 
    (...) 

keyword_handlers = { 'word1': keyword_handler_word1, (...), 'wordN': keyword_handler_wordN } 

次に、実際の処理のコードで:

for word in keywords: 
    # keyword_handlers[word] is a function 
    keyword_handlers[word](line) 
+0

これは非常に単純なケースでは機能しますが、一貫性のある方法でキーワードをチェックしない場合があります(2〜3個のキーワードをチェックする必要があることもあります)別の方法で。 – Steve

+0

@スティーブ:私の編集を見てください。その場合は、関数の辞書を使用して、各キーワードに対して適切な関数を動的に呼び出すことになります。関数呼び出しのオーバーヘッドがありますが、辞書検索は高速です。そして概念的には、私の意見ではこれははるかに明確で清潔です。 –

+0

私はこれについても考えていました。私はそれを試して結果を報告します! – Steve

1
def create_parser(fruits): 
    def parse_txt(f): 
     d = {} 
     for line in f: 
      if not line: 
       pass 
      elif line.startswith('End of section'): 
       return d 
      else: 
       for testfruit in fruits: 
        if testfruit in line: 
         d[testfruit] = True 

テスト機能を動的に作成します。

はあなたが本当に何をしたいのかに応じて、当然のことながら、複雑さの1つのレベルを削除し、functools.partial

def parse_txt(f, fruits): 
    [...] 

または

def parse_txt(fruits, f): 
    [...] 

と仕事を定義するpossibeです。

2

正規表現を使用します。次のような何か:あなたの 'ココス' に

fruit = set(['cocos', 'apple', 'lime']) 
need = set (['cocos', 'pineapple']) 
need. intersection(fruit) 

リターン:

>>> lookup = {'a': 'apple', 'b': 'banane'} # keyword: characters to look for 
>>> pattern = '|'.join('(?P<%s>%s)' % (key, val) for key, val in lookup.items()) 
>>> re.search(pattern, 'apple aaa').groupdict() 
{'a': 'apple', 'b': None} 
0

あなたはこのように、セットの構造を使用することができます。

関連する問題