2017-03-17 11 views
2

私は、複数の引数をとり、呼び出し可能なラムダ関数を返す関数を作成しようとしています。私は、これらのラムダ関数をBeautifulSoupのfind_allメソッドに渡して、htmlを解析しています。ここで動的に作成された関数を返す

は、私はラムダ関数を生成するために書いた関数である。

def tag_filter_function(self, name="", search_terms={}, attrs=[], **kwargs): 

    # filter attrs that are in the search_terms keys out of attrs 
    attrs = [attr for attr in attrs if attr not in search_terms.keys()] 

    # array of strings to compile into a lambda function 
    exec_strings = [] 

    # add name search into exec_strings 
    if len(name) > 0: 
     tag_search_name = "tag.name == \"{}\"".format(name) 
     exec_strings.append(tag_search_name) 

    # add generic search terms into exec_strings 
    if len(search_terms) > 0: 
     tag_search_terms = ' and '.join(["tag.has_attr(\"{}\") and tag[\"{}\"] == \"{}\"".format(k, k, v) for k, v in search_terms.items()]) 
     exec_strings.append(tag_search_terms) 

    # add generic has_attr calls into exec_strings 
    if len(attrs) > 0: 
     tag_search_attrs = ' and '.join(["tag.has_attr(\"{}\")".format(item) for item in attrs]) 
     exec_strings.append(tag_search_attrs) 

    # function string 
    exec_string = "lambda tag: " + " and ".join(exec_strings) 

    return exec(compile(exec_string, '<string>', 'exec')) 

tag_filter_function(name="article", search_terms={"id" : "article"}) 

を呼び出してから返す関数文字列が

lambda tag: tag.name == "article" and tag.has_attr("id") and tag["id"] == "article" 

関数の戻りです値はNoneです。私は、exec()機能が私がここで使用したい機能であると確信していませんが、私は本当にわかりません。この文字列を実行可能なラムダ関数に変換できますか?これについて正しいことをやっているかどうかはわかりません。

+0

あなたがタグに 'has_attr'を使用している場合は、あなたがtag.attr'を探してはいけません:あなたはこのような何かをしたいようなあなたの場合には

def outer_function(): def inner_function(): something_here return inner_function 

は、それが見えます'tag [attr]ではなく? –

答えて

5

execを使う必要は全くありません。関数から関数を返すには、新しい関数を定義してそれを返すだけです。例えば。

def tag_filter_function(self, name="", search_terms={}, attrs=[], **kwargs): 

    # filter attrs that are in the search_terms keys out of attrs 
    attrs = [attr for attr in attrs if attr not in search_terms.keys()] 

    def f(tag): 

     # add name search into exec_strings 
     if len(name) > 0: 
      if tag.name != name: 
       return False 

     # add generic search terms into exec_strings 
     if len(search_terms) > 0: 
      if not all(tag.has_attr(k) and tag[k] == v 
        for k, v in search_terms.items()): 
       return False 

     # add generic has_attr calls into exec_strings 
     if len(attrs) > 0: 
      if not all(tag.has_attr(item) for item in attrs): 
       return False 

     return True 

    return f 
+2

人々の好みは異なりますが、 'functools.partial'は入れ子関数よりも優れていると思います。 – DSM

+0

@ DSMはい、あなたが実際にネスティングを避けたいのであれば、 'functools.partial'は面白いアプローチになります。そして、「内部」関数のインスタンスを1つだけ持つようないくつかの利点がありますが、入れ子が好きです。 – zvone

+0

偉大な答え(私の方はOPの具体的な問題を解決する傾向がありました。問題はすべて)。あなたの答えを編集して、返された関数がOPに必要な引数 'tag'に依存するようにしたいと思うかもしれません。 –

-2

あなたのコードに最小の変更を行いたい場合は、単に試みることができる:

return eval(exec_string) 
をしかし、あなたの問題を解決するためのより良いアプローチのために、あなたはzvoneの提案に従うべきであるし、完全なアプローチを再策定関数を返すことによって。

関連する問題