Is there a “Pythonic” way of creating a list with conditional items?
我在一个真正的django函数中得到了这段代码。如果满足某些条件,则在
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 |
它非常实用。你知道它的作用,那太好了…但是我学会了欣赏
是否有一种更为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] |
但这仍然是对你如何描述逻辑的一个限制,仍然有评估所有价值观的讨厌的副作用,无论情况如何(除非你投了一吨的羔羊肉),我不能真的认为这是一个改善,我们已经开始了。
我不知道为什么我们要附加与函数名匹配的字符串,但是如果这是一个通用模式,我们可以使用它。函数有一个
那么如何:
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]) |
如果其中一个是