如何缩进Python列表理解?


70

列表理解在某些情况下可能很有用,但阅读起来也很恐怖。作为一个稍微夸张的示例,您如何缩进以下内容?

allUuids = [x.id for x in self.db.query(schema.allPostsUuid).execute(timeout = 20) if x.type == "post" and x.deleted is not False]

Answers:


73

这取决于它们有多长时间。我倾向于这样构造它们:

[x.id for x
 in self.db.query(schema.allPostsUuid).execute(timeout=20)
 if x.type == 'post' 
    and x.deleted is not False
    and ...
    and ...]

这样,每个表达式都有自己的一行。

如果任何行变得太大,我希望将其提取为lambda或表达式:

transform = lambda x: x.id
results = self.db.query(schema.allPostsUuid).execute(timeout=20)
condition = lambda x: x.deleted is not False and ... and ...
[transform(x) for x in results if condition(x)]

然后,如果lambda变得太长,它将被提升为函数。


5
这些不是等效的-列表理解要快很多倍,因为它不需要执行任何函数查找。适当的翻译将用于循环。
克劳迪(Claudiu)

2
除了性能方面的问题外,这是一个易于阅读的示例!
地理

1
for循环如何阻止函数查找?还要注意,列表推导中的循环是用C实现的,因此比普通循环快。
orestis

8
仅需注意-您可以使用多个if语句,而不是多次使用“ and”。有点有趣的语法真理。
08年

并非每个使用Python的人都是数据科学家或从事ML的人,因此对于普通人而言,仅使用来自其中仅有几百项内容的API的响应,第二版就可以了。但是我仍然更喜欢第一个。
阿斯凡德·卡兹

50

在我工作的地方,我们的编码准则将使我们执行以下操作:

all_posts_uuid_query = self.db.query(schema.allPostsUuid)
all_posts_uuid_list = all_posts_uuid_query.execute(timeout=20)
all_uuid_list = [
    x.id 
    for x in all_posts_uuid_list 
    if (
        x.type == "post" 
        and 
        not x.deleted  # <-- if you don't care about NULLs / None
    )
]

8
allUuids = [x.id 
            for x in self.db.query(schema.allPostsUuid).execute(timeout = 20) 
            if x.type == "post" and x.deleted is not False]

6

对我来说太多了。也许这只是一个可怕的例子,因为“类型”和“删除”显然是数据库查询的一部分。

我倾向于认为,如果列表理解跨越多行,那么它可能不应该是列表理解。话虽如此,我通常只是像其他人一样在“ if”处拆分内容,并会在此处回答。


3

您不应该为此使用列表理解

列表推导是一个很棒的功能,但是它们只是快捷方式,而不是常规代码。

对于这么长的代码段,您应该使用普通的块:

allUuids = []
for x in self.db.query(schema.allPostsUuid).execute(timeout = 20) :
    if x.type == "post" and x.deleted is not False :
        allUuids.append(x.id)

行为完全相同,可读性更高。Guido为您感到骄傲:-)


1
这有点慢,因为您必须逐步构建阵列
Claudiu

我认为列表理解实际上是完全相同的。这不是因为它是单行表达式,所以这是一次操作...
e-satis

2
电子状态:功能相同,但列表理解速度明显更快
orip

2
在Python文化中,通常会选择可读性而非速度。无论如何,Python并不是一种快速的语言……
e-satis

6
这种方式采用可变的,非功能性的样式。不变性允许您考虑映射和过滤。在此代码中,列表已更改,读者必须记住,列表随后可以更改。最好使用生成器函数yield,或者引入新的变量和函数。
乔治·索维托夫

3

如果您对理解能力有所了解,那么orestis的答案很好。

对于类似的更复杂的理解,我建议使用带有以下内容的生成器yield

allUuids = list(self.get_all_uuids())


def get_all_uuids(self):
    for x in self.db.query(schema.allPostsUuid).execute(timeout = 20):
        if x.type == "post" and x.deleted is not False:
            yield x.id

1

怎么样:

allUuids = [x.id for x in self.db.query(schema.allPostsUuid).execute(timeout = 20) 
                   if (x.type == "post" and x.deleted is not False)]

通常,可以通过将子表达式预先计算为变量来避免长行,这可能会增加很小的性能成本:

query_ids = self.db.query(schema.allPostsUuid).execute(timeout = 20)
allUuids = [x.id for x in query_ids
                   if (x.type == "post" and x.deleted is not False)]

顺便说一句,不是is not False多余吗?您是否担心区分None和False?因为否则,仅将条件保留为:f (x.type == "post" and x.deleted)


1
我同意那个。通常,在所有语言中都应避免使用超过80个字符的行。
Piotr Lesnicki

同意,但是Python的语法规则通常鼓励/强制使用更长的行。通常,它对空格的处理是我对语言的唯一抱怨。
彼得·罗威尔

有了经过精心选择的变量,并用括号或方括号将行分开,我再也不必求助于太长的行(问题更多的是避免使用临时变量self.long_foo.very_long_bar.baz(....))
Piotr Lesnicki

关于“顺便说一句”,它并不是完全多余的,例如x.deleted = None。
阿里·阿夫沙尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.