这个函数的Pythonic等价物?
我有一个从另一种语言移植的函数,你能帮我把它变成“pythonic”吗 在这里,函数以“非pythonic”的方式移植(这是一个人工示例-每个任务都与一个项目或“无”关联,我们需要一个不同项目的列表,不同的意思是从一个任务列表开始,不重复.identifier属性): 我甚至没有特别开始让它成为“pythonic”,以避免一开始就走错了路(例如,列表理解为“if project.identifier not None,filter()基于谓词查找基于字典的标识符注册表,使用set()去除重复项,等等) 编辑: 根据反馈,我有以下几点:这个函数的Pythonic等价物?,python,Python,我有一个从另一种语言移植的函数,你能帮我把它变成“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中拼写得更好foo.has_键(bar)
bar
- 我怀疑这是一个类的
。通常在Python中不需要类,除非您实际在进行数据封装。如果这只是一个普通函数,请将其设置为模块级函数staticmethod
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))