4

別の言語から移植する機能がありますが、「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
4

5 に答える 5

7

このコードについては、非常に非Python的なものは何もありません。いくつかの可能な改善:

  • project_identifiers_seen辞書ではなく、セットである可能性があります。
  • foo.has_key(bar)スペルが良いbar in foo
  • staticmethodこれはクラスのものではないかと思います。通常、実際にデータのカプセル化を行っていない限り、Pythonのクラスは必要ありません。これが単なる通常の機能である場合は、モジュールレベルの機能にします。
于 2012-05-30T16:16:52.290 に答える
3
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返す)および(b)マッピング句(最初の句)がif句がである場合にのみ実行されるためです。NoneorTrue

リスト内包表記に含まれている必要がある理由はありません。ループとして設定することもできますが、実際にそうすることをお勧めします。この方法には、リストを作成しているだけで、リストに何が含まれているのかが明確であるという利点があります。

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構築を回避するために、への2つのアクセスを残しましたtask.project.identifier。これは、promiseライブラリを使用することで簡単に排除できます。


このバージョンでは、promiseを使用して、if/continueを含める必要なしにtask.project.identifierへの繰り返しアクセスを回避します。

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

チェックtask.project.identifierされる前にアクセスされることはないため、これはAttributeErrorsから安全です。task.project

于 2012-05-30T16:18:49.773 に答える
3

どうですか:

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()
于 2012-05-30T16:21:24.990 に答える
1

EAFPはpythonicであると言う人もいるので、次のようになります。

@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()

コースの明示的なチェックも間違いではなく、もちろん、多くのタスクが予測されていない場合はより良いでしょう。

そして、見られた識別子とプロジェクトを追跡するための1つのdictで十分です。プロジェクトの順序が重要な場合は、OrderedDict(python2.7 +)が便利です。

于 2012-05-30T16:30:20.510 に答える
1

すでにたくさんの良い答えがあります、そして確かに、あなたはそれを受け入れました!しかし、私はもう1つのオプションを追加すると思いました。多くの人が、ジェネレータ式やリスト内包表記を使用してコードをよりコンパクトにできることを知っています。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これは、すべてのプロジェクトで元のコードの行を一度に実行することと考えることができます。2番目のジェネレータ式は、反復可能な(project_id, project)タプルを作成します。最後のif句はNone値を除外します。フィルタを通過した(p.identifier, p)場合にのみ評価されます。一緒に、これらの2つのジェネレータ式は、最初の2つのブロック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))
于 2012-05-30T21:31:37.387 に答える