Python zip和zip_之间有中间地带吗`

Python zip和zip_之间有中间地带吗`,python,Python,假设我有以下三个列表: a = [1, 2, 3, 4] b = [5, 6, 7, 8, 9] c = [10, 11, 12] 是否有内置功能,以便: somezip(a, b) == [(1, 5), (2, 6), (3, 7), (4, 8)] somezip(a, c) == [(1, 10), (2, 11), (3, 12), (4, None)] 行为介于zip和zip\u longest之间?没有,但是您可以轻松地将和的功能结合起来以实现您想要的功能 import ite

假设我有以下三个列表:

a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = [10, 11, 12]
是否有内置功能,以便:

somezip(a, b) == [(1, 5), (2, 6), (3, 7), (4, 8)]
somezip(a, c) == [(1, 10), (2, 11), (3, 12), (4, None)]

行为介于
zip
zip\u longest
之间?

没有,但是您可以轻松地将和的功能结合起来以实现您想要的功能

import itertools as it

somezip = lambda *x: it.islice(it.izip_longest(*x), len(x[0]))



>>> list(somezip(a,b))
[(1, 5), (2, 6), (3, 7), (4, 8)]

>>> list(somezip(a,c))
[(1, 10), (2, 11), (3, 12), (4, None)]
from itertools import takewhile, izip_longest
from operator import itemgetter
somezip = lambda *p: list(takewhile(itemgetter(0),izip_longest(*p)))
(如果第一个迭代器的项的计算结果为False,则可以使用lambda表达式替换itemgetter-请参阅@ovgolovin的注释)

例子

>>> from itertools import takewhile, izip_longest
>>> from operator import itemgetter
>>> a = [1, 2, 3, 4]
>>> b = [5, 6, 7, 8, 9]
>>> c = [10, 11, 12]
>>> somezip(a,b)
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> somezip(a,c)
[(1, 10), (2, 11), (3, 12), (4, None)]
>>> somezip(b,c)
[(5, 10), (6, 11), (7, 12), (8, None), (9, None)]

定义您自己的功能:

In [64]: def myzip(*args):
    lenn=len(args[0])
    return list(izip_longest(*[islice(x,lenn) for x in args],fillvalue=None))
   ....: 

In [30]: myzip(a,b)
Out[30]: [(1, 5), (2, 6), (3, 7), (4, 8)]

In [31]: myzip(b,c)
Out[31]: [(5, 10), (6, 11), (7, 12), (8, None), (9, None)]

In [32]: myzip(a,c)
Out[32]: [(1, 10), (2, 11), (3, 12), (4, None)]

您的输出似乎受到第一个迭代器it1的输出的限制。因此,我们可以按原样使用
it1
,用无限
None
填充
it2
——生成迭代器,然后使用
zip
生成迭代器

>>> from itertools import repeat,izip,chain
>>> somezip = lambda it1,it2: izip(it1,chain(it2,repeat(None)))

>>> list(somezip(a,b))
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> list(somezip(a,c))
[(1, 10), (2, 11), (3, 12), (4, None)]
创建无限产生
None
的迭代器

粘合
it2
重复(无)

izip
将在
it1
耗尽后立即停止屈服

其他解决方案也有一些缺陷(我在评论中留下了评论)。它们可能工作得很好,但如果有一些输入,它们可能会意外失败


正如注释中建议的那样,此函数更愿意接受参数中可变数量的迭代器

因此,我更新了代码以实现以下目的:

from itertools import repeat,izip,chain,imap
somezip = lambda it1,*its: izip(it1,*imap(chain,its,repeat(repeat(None))))
测试:

我不得不在这里使用
imap
,尽管没有必要这样做(因为参数稍后会被解包,所以普通的
map
就可以了)。原因是
map
,而
imap
在消耗最小迭代器时停止

因此,
imap
chain
应用于除第一个迭代器之外的所有迭代器,并且
chain
使用
repeat(None)
对每个迭代器进行排序。为了服务于
的每个迭代器,我在
repeat(None)
上面使用了另一个
repeat
(注意,这在其他项目中可能非常危险,因为外部
repeat
生成的所有对象都是相同的对象
repeat(None)
,因此最终所有
的迭代器都共享它)。然后,我解包
imap
对象,生成
izip
的参数,该参数返回值,直到
it1
被消费(因为
chain
ed
its
现在会生成每个值的无限序列)

请注意,所有操作都在纯C中工作,因此不涉及解释器开销

为了阐明其工作原理,我添加了以下详细说明:

def somezip(it1,*its): #from 0 to infinite iterators its
    # it1 -> a1,a2,a3,...,an
    # its -> (b1,b2,b3,...,bn),(c1,c2,c3,...,cn),...
    infinite_None = repeat(None) # None,None,None,...
    infinite_Nones = repeat(infinite_None) # infinite_None,infinite_None,... (share the same infinite_None)
    chained = imap(chain,its,infinite_Nones) # [(b1,b2,b3,...,bn,None,None,...),(c1,c2,c3,...,cn,None,None,...),...]
    return izip(it1,*chained)
它的一个衬里就是:

somezip = lambda it1,*its: izip(it1,*imap(chain,its,repeat(repeat(None))))

这比其他的要长,但相对容易理解,如果这很重要的话

输出:

somezip(a,b):[(1,5)、(2,6)、(3,7)、(4,8)]
somezip(a,c):[(1,10),(2,11),(3,12),(4,无)]
somezip(a,g(2)):[(1,0),(2,1),(3,无),(4,无)]
somezip(g(2),a):[(1,1)]
somezip(a,b,c):[(1,5,10),(2,6,11),(3,7,12),(4,8,无)]
somezip(a,b,c,g(2)):[(1,5,10,0),(2,6,11,1),(3,7,12,无),(4,8,无,无)]
somezip(g(2),a,b,c):[(1,1,5,10)]

换句话说,一个始终取第一个参数长度的zip?@jpm:Correct。我想这是一个更好的解释,不是先调用
zip\u longest
然后剪切结果吗?如果
x
中的一个是无限迭代器呢?@JoachimSauer-no.
list(it.islice(it.count(),10))
返回一个短列表。哦,我不知道,太好了!如果第一个迭代器不是列表,
len
将进行额外的传递以确定长度。此外,它将使用迭代器。(在问题中,OP表示输入是列表。我只是想对其他人澄清这一点,他们可能会发现这个问题与他们的任务相关)。@ovgolovin:我不明白。如果
x[0]
是一个生成器,比如说,你不需要额外的过程来确定长度,你会不会得到
TypeError:type'generator'的对象没有len()
?如果第一个迭代器的元素计算结果为
False
?如果第一个迭代器的元素计算结果为
None
呢在我看来,这似乎是安全的,这个解决方案将需要为填充创建临时的唯一元素。因为如果我们使用
None
,第一个列表也可以包含
None
,并且我们无法区分
izip__longes
返回的
None
和迭代器中预先包含的
None
。一个小的通用性损失是fillvalue必须是
None
+1。最好的方法之一,因为它不需要调用
len(it1)
。但也必须有办法支持任意数量的iterables…@glglglgl谢谢!我以为只有两个迭代器。我将为任意数量的迭代器添加代码。但是,如果我们能使其通用化,在我看来这将是一个好处,因为
zip()
izip()
izip\u longest()
都接受超过2个…@glglglglgl我添加了可变版本!
def somezip(it1,*its): #from 0 to infinite iterators its
    # it1 -> a1,a2,a3,...,an
    # its -> (b1,b2,b3,...,bn),(c1,c2,c3,...,cn),...
    infinite_None = repeat(None) # None,None,None,...
    infinite_Nones = repeat(infinite_None) # infinite_None,infinite_None,... (share the same infinite_None)
    chained = imap(chain,its,infinite_Nones) # [(b1,b2,b3,...,bn,None,None,...),(c1,c2,c3,...,cn,None,None,...),...]
    return izip(it1,*chained)
somezip = lambda it1,*its: izip(it1,*imap(chain,its,repeat(repeat(None))))
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = [10, 11, 12]
def g(n): return xrange(n)  # simple generator

def my_iter(iterable, fillvalue=None):
    for i in iterable: yield i
    while True: yield fillvalue

def somezip(*iterables, **kwds):
    fillvalue = kwds.get('fillvalue')
    iters = [my_iter(i, fillvalue) for i in iterables]
    return [tuple(next(it) for it in iters) for i in iterables[0]]

print 'somezip(a, b):', somezip(a, b)
print 'somezip(a, c):', somezip(a, c)
print 'somezip(a, g(2)):', somezip(a, g(2))
print 'somezip(g(2), a):', somezip(g(2),a)
print 'somezip(a, b, c):', somezip(a, b, c)
print 'somezip(a, b, c, g(2)):', somezip(a, b, c, g(2))
print 'somezip(g(2), a, b, c):', somezip(g(2), a, b, c)