簡體   English   中英

Pythonic相當於這個功能?

[英]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()

有人說EAFPpythonic ,所以:

@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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM