pythonically基于同一列表的元素组的python列表的子集

2024-04-26 00:51:11 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个平铺的数字列表,逻辑上是,一组3个,其中每个三元组是(数字,\u忽略,标志[0或1]),例如:

[7,56,1, 8,0,0, 2,0,0, 6,1,1, 7,2,0, 2,99,1]

我想(pythonically)处理这个列表,根据'flag'的值创建一个新的数字列表:如果'flag'是1,那么我想要'number',否则是0。因此,上面的列表将变成:

[7, 0, 0, 6, 0, 2]

我最初的尝试是:

list = [7,56,1, 8,0,0, 2,0,0, 6,1,1, 7,2,0, 2,99,1]
numbers = list[::3]
flags = list[2::3]

result = []
for i,number in enumerate(numbers):
  result.append(number if flags[i] == 1 else 0)

这是可行的,但在我看来,应该有更好的方法从列表中清晰地提取元组。比如:

list = [7,56,1, 8,0,0, 2,0,0, 6,1,1, 7,2,0, 2,99,1]
for (number, __, flag) in list:
    ...etc

但我好像做不到。你知道吗

我可以循环浏览整个列表:

result = []
for i in range(0, len(list), 3):
    result.append(list[i] if list[i+2] == 1 else 0)

看起来更小更有效。你知道吗

我不清楚这里的最佳选择。如有任何建议,将不胜感激。你知道吗

注意:我接受了wim的回答:

[L[i]*L[i+2] for i in range(0, len(L), 3)]

但我想重申的是,wims和暗影突击队的反应都是有效的。我接受wim的答案是基于简单和清晰(以及与python2的兼容性,尽管ShadowRanger指出zip也在Py2中,所以这个基础是无效的)。你知道吗

暗影游侠的回答是:

[number if flag else 0 for number, _, flag in zip(*[iter(mylist)]*3)]

也确实做了我认为我想要的(提供元组),但有点晦涩,需要zip。正如wim所指出的,ShadowRanger的答案非常适合于数据流而不是固定的列表。你知道吗

我还要注意的是ShadowRanger的答案增加了zip()的模糊用法(随着时间的推移,它会变得不那么模糊),但是通过使用元组的命名值来增加清晰度,所以这有点胜负。你知道吗

对于那些努力理解zip(*[iter(mylist)]*3)]的人来说,它创建了一个迭代器的三个副本,然后用来构造元组。因为它是同一个迭代器,所以每次使用都会推进迭代器,使元组完全符合我的要求。你知道吗

为了清楚和通用,我也有点倾向于@ShadowRanger的解决方案的修改版本:

i = iter(mylist)
[number if flag else 0 for number, _, flag in zip(i, i, i)]

(在我看来,这似乎不那么晦涩)。你知道吗


Tags: 答案innumber列表forif数字result
3条回答

因为您有逻辑三元组,所以可以使用iter、序列乘法和zip来实现您的目标:

result = []
for number, _, flag in zip(*[iter(mylist)]*3):
    result.append(number if flag else 0)  # flag is only 1 or 0, so no need to compare it

它将mylist上的相同的迭代器解压为zip的三个参数;因为它是同一个迭代器,zip将元素0、1和2解压为第一个输出,然后3、4和5等等。然后循环将这三个元素解压为逻辑名称(使用_表示您不关心的值)。你知道吗

列表理解甚至可以是一行:

result = [number if flag else 0 for number, _, flag in zip(*[iter(mylist)]*3)]

虽然这有点复杂,意思是明智的。你知道吗

这种方法的优点是:

  1. 它与任何iterable一起工作,而不仅仅是list
  2. 它的性能很好;输入只遍历一次,任何切片解决方案都会遍历它多次
  3. 它使用有意义的名称,而不是索引偏移量的匿名幻数

缺点:

  1. zip(*[iter(mylist)]*3)有点神奇
  2. 如果输入的长度不是三的倍数,它将自动忽略数据(最后的部分组被删除)

注意:在任何类似于生产代码的代码中,不要内联zip/iter/unpack技巧。使用the ^{} recipe from the ^{} module(或基于zip的变体)并调用:

# Defined somewhere else for common use
def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x')  > ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.zip_longest(*args, fillvalue=fillvalue)

for number, _, flag in grouper(mylist, 3):

配方是经过测试和预测的,通过给它起一个名字,你可以让使用它的代码更加明显。你知道吗

我认为最直接的方法是简单的列表理解:

>>> [L[i]*L[i+2] for i in range(0, len(L), 3)]
[7, 0, 0, 6, 0, 2]

或者考虑numpy,它对于这样的任务非常强大:

>>> import numpy as np
>>> a = np.array(L).reshape(-1, 3).T
>>> a
array([[ 7,  8,  2,  6,  7,  2],
       [56,  0,  0,  1,  2, 99],
       [ 1,  0,  0,  1,  0,  1]])
>>> a[0]*a[2]
array([7, 0, 0, 6, 0, 2])

我想这可能有用:

>>> list_ = [7,56,1, 8,0,0, 2,0,0, 6,1,1, 7,2,0, 2,99,1]
>>> [a if c == 1 else 0 for a,c in zip(list_[::3],list_[2::3])]
... 
 [7, 0, 0, 6, 0, 2]

相关问题 更多 >