![](/img/trans.png)
[英]What is the 'pythonic' equivalent to the 'fold' function from functional programming?
[英]Pythonic equivalent of this function?
我有一個從另一種語言移植的功能,你能幫我把它變成“pythonic”嗎?
這里的函數以“非pythonic”方式移植(這是一個人為的例子 - 每個任務都與一個項目或“無”相關聯,我們需要一個不同項目的列表,不同意味着沒有重復的.identifier屬性,從任務列表開始):
@staticmethod
def get_projects_of_tasks(task_list):
projects = []
project_identifiers_seen = {}
for task in task_list:
project = task.project
if project is None:
continue
project_identifier = project.identifier
if project_identifiers_seen.has_key(project_identifier):
continue
project_identifiers_seen[project_identifier] = True
projects.append(project)
return projects
我特別沒有開始讓它“pythonic”不要從錯誤的腳開始(例如列表理解“如果project.identifier不是None,filter()基於查找基於字典的標識符注冊表的謂詞,使用set()去除重復項等)
編輯:
根據反饋,我有這個:
@staticmethod
def get_projects_of_tasks(task_list):
projects = []
project_identifiers_seen = set()
for task in task_list:
project = task.project
if project is None:
continue
project_identifier = project.identifier
if project_identifier in project_identifiers_seen:
continue
project_identifiers_seen.add(project_identifier)
projects.append(project)
return projects
關於這段代碼沒有任何大量的非語言。 一些可能的改進:
project_identifiers_seen
可以是一個集合,而不是字典。 foo.has_key(bar)
bar in foo
拼寫更好 staticmethod
方法。 除非你實際上在進行數據封裝,否則通常不需要Python中的類。 如果這只是一個普通函數,請將其設置為模塊級函數。 def get_projects_of_tasks(task_list):
seen = set()
return [seen.add(task.project.identifier) or task.project #add is always None
for task in task_list if
task.project is not None and task.project.identifier not in seen]
這是因為(a) add
返回None
(和or
返回最后一個表達式的值)和(b)映射子句(第一個子句)僅在if子句為True
。
沒有理由它必須在列表理解中 - 你也可以將其設置為循環,事實上你可能更喜歡。 這種方式的優點是很明顯,您只是在構建一個列表,以及它應該包含在哪個列表中。
我沒有使用staticmethod
因為很少需要它。 將此作為模塊級函數或classmethod
。
另一種選擇是生成器(感謝@delnan指出這一點):
def get_projects_of_tasks(task_list):
seen = set()
for task in task_list:
if task.project is not None and task.project.identifier not in seen:
identifier = task.project.identifier
seen.add(identifier)
yield task.project
這消除了理解中的副作用(這是有爭議的)的需要,但是要清楚地收集正在收集的內容。
為了避免另一個if / continue構造,我留下了兩次訪問task.project.identifier
。 使用promise庫可以方便地消除這種情況。
此版本使用promises來避免重復訪問task.project.identifier,而無需包含if / continue:
from peak.util.proxies import LazyProxy, get_cache # pip install ProxyTypes
def get_projects_of_tasks(task_list):
seen = set()
for task in task_list:
identifier = LazyProxy(lambda:task.project.identifier) # a transparent promise
if task.project is not None and identifier not in seen:
seen.add(identifier)
yield task.project
這是AttributeErrors安全的,因為task.project.identifier
是從來沒有訪問之前task.project
檢查。
關於什么:
project_list = {task.project.identifier:task.project for task in task_list if task.project is not None}
return project_list.values()
對於2.6-使用dict構造函數:
return dict((x.project.id, x.project) for x in task_list if x.project).values()
@staticmethod
def get_projects_of_tasks(task_list):
projects = {}
for task in task_list:
try:
if not task.project.identifier in projects:
projects[task.project.identifier] = task.project
except AttributeError:
pass
return projects.values()
對於cours來說,明確的檢查也沒有錯,如果許多任務沒有投射,那當然會更好。
只有一個字典來跟蹤看到的標識符和項目就足夠了,如果項目的順序很重要,那么OrderedDict
(python2.7 +)可以派上用場。
已經有很多好的答案,而且,你確實接受了一個! 但我想我會再增加一個選項。 許多人已經看到使用生成器表達式或列表推導可以使您的代碼更緊湊。 我將建議使用生成器表達式進行初始過濾的混合樣式,同時在最終過濾器中保持for
循環。
這種風格優於原始代碼風格的優點是它通過消除continue
語句簡化了控制流程。 這種樣式優於單個列表理解的優點是它避免了以自然方式對task.project.identifier
多次訪問。 它還透明地處理可變狀態( seen
集合),我認為這很重要。
def get_projects_of_tasks(task_list):
projects = (task.project for task in task_list)
ids_projects = ((p.identifier, p) for p in projects if p is not None)
seen = set()
unique_projects = []
for id, p in ids_projects:
if id not in seen:
seen.add(id)
unique_projects.append(p)
return unique_projects
因為這些是生成器表達式(括在括號中而不是括號中),所以它們不構建臨時列表。 第一個生成器表達式創建一個可迭代的項目; 您可以將其視為同時在所有項目中執行原始代碼中的project = task.project
行。 第二個生成器表達式創建一個可迭代的(project_id, project)
元組。 最后的if
子句過濾掉None
值; (p.identifier, p)
僅在p
通過濾波器時進行評估。 這兩個生成器表達式一起消除了前兩個if
塊。 其余代碼與您的代碼基本相同。
另請注意Marcin / delnan的優秀建議,即使用yield
創建生成器。 這進一步減少了代碼的冗長程度,將其歸結為其基本要素:
def get_projects_of_tasks(task_list):
projects = (task.project for task in task_list)
ids_projects = ((p.identifier, p) for p in projects if p is not None)
seen = set()
for id, p in ids_projects:
if id not in seen:
seen.add(id)
yield p
唯一的缺點 - 如果這不明顯 - 是如果你想永久存儲項目,你必須將生成的iterable傳遞給list
。
projects_of_tasks = list(get_projects_of_tasks(task_list))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.