【问题标题】:zip list with a single element带有单个元素的 zip 列表
【发布时间】:2016-07-31 21:01:20
【问题描述】:

我有一些元素的列表,例如[1, 2, 3, 4] 和单个对象,例如'a'。我想生成一个元组列表,列表的元素位于第一个位置,单个对象位于第二个位置:[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]

我可以像这样用zip 做到这一点:

def zip_with_scalar(l, o): # l - the list; o - the object
    return list(zip(l, [o] * len(l)))

但是,这给了我一种创建和不必要的重复元素列表的感觉。

另一种可能是

def zip_with_scalar(l, o):
    return [(i, o) for i in l]

这确实非常干净和pythonic,但在这里我“手动”完成了整个事情。在 Haskell 中,我会做类似的事情

zipWithScalar l o = zip l $ repeat o

是否有任何内置函数或技巧,无论是使用标量压缩还是使我能够使用普通压缩,即无限列表排序?

【问题讨论】:

  • jonrsharpe 说了什么,但您可能还想将其重写为生成器,而不是常规函数。
  • @OmnipotentEntity,因为 OP 使用的是 3.x,所以他们只能 return zip(l, repeat(o));它是一个迭代器,不需要yield
  • 好点,我错过了标签。
  • 小心 o 的可变值,否则你可能会得到一些惊喜

标签: python list python-3.x


【解决方案1】:

这是最接近您的 Haskell 解决方案的:

import itertools

def zip_with_scalar(l, o):
    return zip(l, itertools.repeat(o))

您也可以使用生成器,避免像推导那样创建列表:

def zip_with_scalar(l, o):
    return ((i, o) for i in l)

【讨论】:

    【解决方案2】:

    可以使用内置的map函数:

    >>> elements = [1, 2, 3, 4]
    >>> key = 'a'
    >>> map(lambda e: (e, key), elements)
    [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
    

    【讨论】:

      【解决方案3】:

      对于itertools.cycle 类来说,这是一个完美的工作。

      from itertools import cycle
      
      
      def zip_with_scalar(l, o):
          return zip(i, cycle(o))
      

      演示:

      >>> from itertools import cycle
      >>> l = [1, 2, 3, 4]
      >>> list(zip(l, cycle('a')))
      [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
      

      【讨论】:

      • cycle 的参数必须是可迭代的,不是吗?它适用于'a',因为它是字符串,但不适用于任意对象。
      • @zegkljan 是的,它必须是可迭代的。
      • 仅当要映射到的对象是长度为 1 的可迭代对象时,cycle 才会给出此问题所要求的结果。如果对字符串 'ab' 而不是 'a' 执行相同操作,则会得到以下结果:[(1, 'a'), (2, 'b'), (3, 'a'), (4, 'b')]
      • 字符串是可迭代的,如果你想无限期地返回字符串,你应该把它放到一个容器中。 list 会很好。 list(zip(range(20), cycle(["ab"])))@Swier
      【解决方案4】:
      lst = [1,2,3,4]
      tups = [(itm, 'a') for itm in lst]
      tups
      
      > [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
      

      【讨论】:

        【解决方案5】:

        您也可以使用 zip_longest,填充值为 o

        from itertools import zip_longest
        
        def zip_with_scalar(l, o): # l - the list; o - the object
            return zip_longest(l, [o], fillvalue=o)
        
        print(list(zip_with_scalar([1, 2, 3, 4] ,"a")))
        

        请注意,用于o 的任何可变值都不会被复制,无论是使用 zip_longest 还是重复。

        【讨论】:

          【解决方案6】:
          >>> l = [1, 2, 3, 4]
          >>> list(zip(l, "a"*len(l)))
          [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
          

          【讨论】:

            【解决方案7】:

            只需定义一个具有无限迭代器的类,它会使用您要在列表中注入的单个元素进行初始化:

            class zipIterator:
                def __init__(self, val):
                    self.__val = val
            
                def __iter__(self):
                    return self
            
                def __next__(self):
                    return self.__val
            

            然后从这个类和你拥有的列表中创建你的新列表:

            elements = [1, 2, 3, 4]
            key = 'a'
            res = [it for it in zip(elements, zipIterator(key))]
            

            结果是:

            >>res
            [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
            

            【讨论】:

              【解决方案8】:

              more-itertools库最近添加了一个zip_broadcast()函数很好的解决了这个问题:

              >>> from more_itertools import zip_broadcast
              >>> list(zip_broadcast([1,2,3,4], 'a'))
              [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
              

              这是一个比此处发布的其他答案更通用的解决方案:

              • 正确处理空的可迭代对象。
              • 可以有多个可迭代和/或标量参数。
              • 不需要知道标量/可迭代参数的顺序。
              • 如果有多个可迭代参数,您可以使用strict=True 检查它们的长度是否相同。
              • 您可以轻松控制是否应将字符串视为可迭代对象(默认情况下不是)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-12-06
                相关资源
                最近更新 更多