2017-03-17 99 views
2

我試圖創建一個函數,它接受多個參數,並返回一個可調用的lambda函數。我將這些lambda函數傳遞到BeautifulSoup的find_all方法中以解析html。返回動態創建函數

這裏是我寫生成lambda函數功能:

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()函數是我想在這裏使用的,但我真的不確定。將該字符串轉換爲可執行的lambda函數是可能的,如果是這樣的話?不知道我是否以正確的方式開展這項工作。

+0

如果你使用'標籤上has_attr',你不應該找'tag.attr '而不是'tag [attr]'? –

回答

5

絕對不需要使用exec。要從一個函數返回一個函數,只需定義一個新函數並返回它。例如。

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

在你的情況下,它看起來像你想要做這樣的事情:

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'會是一個有趣的方法。它確實有一些好處,比如只有一個「內部」函數實例,但我喜歡嵌套:) – zvone

+0

很好的答案(我的意圖是解決OP的具體問題,而你的更好,因爲它教育更好的方法這個問題一起)。我想你可能想要編輯你的答案,使得返回的函數依賴於OP所要求的參數'tag'。 –

-2

如果你想最小的改變你的代碼,你可以簡單地嘗試:

return eval(exec_string) 

但對於一個更好的方法來解決你的問題,你應該遵循zvone的建議和完全重新制訂的辦法通過返回一個函數。