Python - 仅压缩包含数据的列表

Python - zip only lists that contain data

我有一个脚本可以将数据从一种类型转换为另一种类型。 源文件可以有以下一项、两项或全部:位置、旋转和缩放数据。

我的脚本在对输出文件进行转换后将 3 个文件压缩在一起。

在这种情况下,我的源文件只包含位置数据。 所以最后 returned 的列表是:

pData = [['-300.2', '600.5'],['150.12', '280.7'],['19.19', '286.56']]
rData = []
sData = []

translationData = list(zip(pData, rData, sData))

如果我尝试这个,它会 return [] 因为最短的列表是 []。 如果我尝试:

translationData = list(zip_longest(pData, rData, sData))

我得到:

`[(['-300.2', '600.5'], None, None), (['150.12', '280.7'], None, None), (['19.19', '286.56'], None, None)]`

有没有办法只压缩包含数据的列表,或者从列表的元组中删除 None

提前致谢!

您可以使用嵌入在 list-comp 中的 filter 内置函数。

Note: In Python 3 filter returns an iterator, so you will need to call tuple() on it. (unlike in py2)

pData = [['-300.2', '600.5'],['150.12', '280.7'],['19.19', '286.56']]
rData = []
sData = []

from itertools import zip_longest  # izip_longest for python 2
[tuple(filter(None, col)) for col in zip_longest(pData, rData, sData)]

结果:

[(['-300.2', '600.5'],), (['150.12', '280.7'],), (['19.19', '286.56'],)]

我会尝试添加更多变化-bool,None,lambda.

import itertools
from itertools import zip_longest
pData = [['-300.2', '600.5'],['150.12', '280.7'],['19.19', '286.56']]
rData = []
sData = []


print ([list(filter(bool, col)) for col in zip_longest(pData, rData, sData)])
print ([list(filter(None, col)) for col in zip_longest(pData, rData, sData)])
print ([list(filter(lambda x: x, col)) for col in zip_longest(pData, rData, sData)])

输出-

[[['-300.2', '600.5']], [['150.12', '280.7']], [['19.19', '286.56']]]
[[['-300.2', '600.5']], [['150.12', '280.7']], [['19.19', '286.56']]]
[[['-300.2', '600.5']], [['150.12', '280.7']], [['19.19', '286.56']]]

您可以根据 documentation 修改 zip_longest 的纯 Python 版本,并创建一个版本来执行您想要的操作:

from itertools import chain, repeat

class ZipExhausted(Exception):
    pass

def zip_longest(*args, **kwds):
    # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    counter = len(args) - 1
    def sentinel():
        nonlocal counter
        if not counter:
            raise ZipExhausted
        counter -= 1
        yield fillvalue
    fillers = repeat(fillvalue)
    iterators = [chain(it, sentinel(), fillers) for it in args]
    try:
        while iterators:
            res = []
            for it in iterators:
                value = next(it)
                if value != fillvalue:
                    res.append(value)
            yield tuple(res)
    except ZipExhausted:
        pass

pData = [['-300.2', '600.5'],['150.12', '280.7'],['19.19', '286.56']]
rData = []
sData = []

translationData = list(zip_longest(pData, rData, sData))
print(translationData)

输出:

[(['-300.2', '600.5'],), (['150.12', '280.7'],), (['19.19', '286.56'],)]

如果您出于某种原因不想导入或使用列表理解等:

  1. 对要压缩的列表进行分组(所有列表)
  2. 然后遍历分组以检查每个分组中是否有任何内容
  3. 将其中包含数据的一组附加到一起(可压缩)
  4. 最后,*通过过滤分组的 zip (*zippable)

    alist = ['hoop','joop','goop','loop']
    blist = ['homp','jomp','gomp','lomp']
    clist = []
    dlist = []
    
    allLists = [alist,blist,clist,dlist]
    
    zippable = []
    
    for fullList in allLists:
        if fullList:
            zippable.append(fullList)
    
    finalList = list(zip(*zippable))
    
    print(finalList)
    

另一种可能的解决方案

如果列表完全是空的或完全是满的,这将起作用:

>>> list(zip(*(x for x in (pData, rData, sData) if x)))
[(['-300.2', '600.5'],), (['150.12', '280.7'],), (['19.19', '286.56'],)]

如果你不想要 None 你可以使用 zip_longest 的 key-word 参数 fillvalue 来代替你想要的任何东西,这样你可以获得统一的结果正如@Ale

所指出的
>>> translationData = list(zip_longest(pData, rData, sData, fillvalue=tuple()))
>>> translationData
[(['-300.2', '600.5'], (), ()), (['150.12', '280.7'], (), ()), (['19.19', '286.56'], (), ())]
>>> 

如果您使用可变对象作为填充值,请当心,因为如果您更改一个对象,所有对象都会更改,因为它们都是对同一对象的引用

>>> translationData = list(zip_longest(pData, rData, sData,fillvalue=list()))
>>> translationData
[(['-300.2', '600.5'], [], []), (['150.12', '280.7'], [], []), (['19.19', '286.56'], [], [])]
>>> translationData[0][1].append(23)
>>> translationData
[(['-300.2', '600.5'], [23], [23]), (['150.12', '280.7'], [23], [23]), (['19.19', '286.56'], [23], [23])]
>>>