这个函数的Pythonic等价物?

这个函数的Pythonic等价物?,python,Python,我有一个从另一种语言移植的函数,你能帮我把它变成“pythonic”吗 在这里,函数以“非pythonic”的方式移植(这是一个人工示例-每个任务都与一个项目或“无”关联,我们需要一个不同项目的列表,不同的意思是从一个任务列表开始,不重复.identifier属性): 我甚至没有特别开始让它成为“pythonic”,以避免一开始就走错了路(例如,列表理解为“if project.identifier not None,filter()基于谓词查找基于字典的标识符注册表,使用set()去除重复项,

我有一个从另一种语言移植的函数,你能帮我把它变成“pythonic”吗

在这里,函数以“非pythonic”的方式移植(这是一个人工示例-每个任务都与一个项目或“无”关联,我们需要一个不同项目的列表,不同的意思是从一个任务列表开始,不重复.identifier属性):

我甚至没有特别开始让它成为“pythonic”,以避免一开始就走错了路(例如,列表理解为“if project.identifier not 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\u identifiers\u seen
    可以是一个集合,而不是一个字典
  • foo.has_键(bar)
    在foo中拼写得更好
    bar
  • 我怀疑这是一个类的
    staticmethod
    。通常在Python中不需要类,除非您实际在进行数据封装。如果这只是一个普通函数,请将其设置为模块级函数
这是因为(a)
add
返回
None
(和
返回最后计算的表达式的值)和(b)仅当if子句为
True
时才执行映射子句(第一个子句)

没有理由一定要在列表理解中——你也可以把它作为一个循环来设置,事实上你可能更喜欢这样。这种方式的优点是,很明显你只是在构建一个列表,以及列表中应该包含什么

我没有使用
staticmethod
,因为很少需要它。可以将其作为模块级函数,也可以使用
classmethod


另一种选择是发电机(感谢@delnan指出这一点):

这消除了理解过程中的副作用(这是有争议的),但清楚地知道收集的是什么

为了避免另一个if/continue构造,我保留了对
task.project.identifier
的两个访问。这可以通过使用promise库方便地消除


此版本使用承诺避免重复访问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
这对于属性错误是安全的,因为在选中
任务.project
之前,从未访问
任务.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()
当然,显式检查也不会错,如果许多任务都没有项目,当然会更好


如果项目的顺序很重要,那么只需一个dict来跟踪所看到的标识符和项目就足够了,那么a(python2.7+)可以派上用场。

已经有很多很好的答案,事实上,您已经接受了一个!但我想我会再添加一个选项。许多人已经看到,您的代码可以通过生成器表达式或列表理解变得更加紧凑。我将建议使用生成器表达式来执行I初始筛选,同时在最终筛选中为
循环维护

与原始代码的样式相比,此样式的优点在于它通过消除
continue
语句简化了控制流程。与单个列表理解相比,此样式的优点在于它避免了以自然方式多次访问
task.project.identifier
。它还处理可变状态(看到的
是透明的,我认为这很重要

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
因为这些是生成器表达式(用括号而不是括号括起来),它们不构建临时列表。第一个生成器表达式创建项目的iterable;您可以将其视为同时在所有项目上执行原始代码中的
project=task.project
行。第二个生成器表达式创建
的iterable(project\u id,project)
元组。末尾的
if
子句过滤掉
None
值;
(p.identifier,p)
仅在
p
通过过滤器时才计算。这两个生成器表达式一起删除了前两个
if
块。其余代码基本上与您的代码相同

还请注意,您可以使用
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传递给
列表

projects_of_tasks = list(get_projects_of_tasks(task_list))

是的,很有意义。我将用“in”替换HasyKy(),而我将使用SET。不幸的是,没有对“STATICE方法装饰器的控制。还有其他的效率和紧凑性吗?”JARRODROBORSON。我不认为这是模糊的;事实上,我发现简洁的代码更可读。“Python”——“Pythic”的意思是使代码更可靠,而不是将代码打包得如此密集以至于不能理解。154字符行是有问题的IMO。如果它被分解成多行列表理解,我会发现它非常可读。但是即使这样,我也不会考虑在列表理解中改变对象。pythonic”--也许可以接受,但并不完全符合列表理解习惯用法。对于初学者来说,
的一个操作数仅仅是为了产生副作用而存在的,并且总是计算为
None
。这几乎是,但不是完全不同于我所知道的所有Python习惯用法。还有
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
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
projects_of_tasks = list(get_projects_of_tasks(task_list))