关于python:是否有一种”pythonic”方法来创建带有条件项的列表?

Is there a “Pythonic” way of creating a list with conditional items?

我在一个真正的django函数中得到了这段代码。如果满足某些条件,则在list中添加项目。

1
2
3
4
5
6
7
8
9
10
11
ret = []

if self.taken():
    ret.append('taken')

if self.suggested():
    ret.append('suggested')

#.... many more conditions and appends...

return ret

它非常实用。你知道它的作用,那太好了…但是我学会了欣赏listdict的理解的美丽。

是否有一种更为Python式的方式来表达这个结构,也许可以一下子初始化并填充数组?


创建映射字典:

1
2
3
4
self.map_dict = {'taken': self.taken,
                 'suggested': self.suggested,
                 'foo' : self.bar}
[x for x in ['taken', 'suggested', 'foo'] if self.map_dict.get(x, lambda:False)()]

相关的:最有效的方式来做一个if-elif-elif-else语句,什么时候做得最好?


不是很大的进步,但我会提到:

1
2
3
4
5
6
7
def populate():
    if self.taken():
        yield 'taken'
    if self.suggested():
        yield 'suggested'

ret = list(populate())

我们能做得更好吗?我持怀疑态度。显然,除了列表文字外,还需要使用其他语法,因为我们不再具有"1 expression=1 element in result"不变量。

编辑:

我们的数据有一个模式,它是(条件、值)对的列表。我们可能会尝试利用它:

1
2
3
4
5
[value
 for condition, value
 in [(self.taken(), 'taken'),
     (self.suggested(), 'suggested')]
 if condition]

但这仍然是对你如何描述逻辑的一个限制,仍然有评估所有价值观的讨厌的副作用,无论情况如何(除非你投了一吨的羔羊肉),我不能真的认为这是一个改善,我们已经开始了。


我不知道为什么我们要附加与函数名匹配的字符串,但是如果这是一个通用模式,我们可以使用它。函数有一个__name__属性,我认为它总是包含列表中您想要的内容。

那么如何:

1
return [fn.__name__ for fn in (self.taken, self.suggested, foo, bar, baz) if fn()]

如果我正确理解了这个问题,那么对于非成员函数和成员函数来说,这同样适用。

编辑:

好吧,我们添加一个映射字典。并将函数名拆分为元组或列表。

1
2
3
4
5
6
7
8
9
10
11
fns_to_check = (self.taken, self.suggested, foo, bar, baz)

# This holds only the exceptions; if a function isn't in here,
# we will use the .__name__ attribute.
fn_name_map = {foo:'alternate', bar:'other'}

def fn_name(fn):
   """Return name from exceptions map, or .__name__ if not in map"""
    return fn_name_map.get(fn, fn.__name__)

return [fn_name(fn) for fn in fns_to_check if fn()]

您也可以使用@hcwhsa的映射字典答案。这里的主要区别是我建议只映射异常。


对于这个非常具体的例子,我可以这样做:

1
return [x for x in ['taken', 'suggested', ...] if getattr(self, x)()]

但同样,这只在它调用检查的项和方法具有相同的名称(即我的确切代码)时才有效。它可以被改编,但有点硬。我非常愿意接受其他解决方案!


一种选择是使用"sentinel"样式的对象来代替不符合相应条件的列表条目。然后可以定义一个函数来过滤缺少的项:

1
2
3
4
5
6
#"sentinel indicating a list element that should be skipped
Skip = object()

def drop_missing(itr):
   """returns an iterator yielding all but Skip objects from the given itr"""
    return filter(lambda v: v is not Skip, itr)

有了这个简单的机制,我们就相当接近于列出理解风格的语法:

1
2
3
4
5
6
return drop_skips([
    'taken' if self.taken else Skip,
    'suggested' if self.suggested else Skip,
    100 if self.full else Skip,
    // many other values and conditions
])

在另一个实例中(其中一个值将被定义,但可能没有——在我的例子中是一个django模型的字段),我发现只添加它们和过滤就可以工作:

1
return filter(None, [self.user, self.partner])

如果其中一个是None,它们将从列表中删除。它比仅仅检查要密集一点,但仍然是一种相当容易的方法,可以在不写书的情况下清除输出。