【问题标题】:Automatically extend a Python list to N elements if it's too short? [duplicate]如果 Python 列表太短,是否自动将其扩展到 N 个元素? [复制]
【发布时间】:2020-03-26 14:37:26
【问题描述】:

如果列表中的 N 元素少于 N 元素,那么在 Python 中自动将列表扩展到 N 元素的最佳方法是什么?

也就是说,让我有这个字符串:s = "hello there"。如果我这样做:

x, y, z = s.split()

我会得到一个错误,因为s.split() 返回一个包含两个元素的列表,但我将它分配给 3 个变量。我想要的是为z 分配None

我知道我可以做到这一点很困难:

l = s.split()
while len(l) < 3:
    l.append(None)
x, y, z = l

但一定有比这更优雅的东西。

【问题讨论】:

标签: python list


【解决方案1】:

如果您想要单线,请执行以下操作:

s = "hello there"
x, y, z, *_ = s.split() + [None, None, None]

print(x, y, z)

输出

hello there None

请注意,单行代码不一定更易读也不一定更优雅。感谢@Grismar,一个变体是:

x, y, z, *_ = s.split() + [None] * 3

【讨论】:

  • 根据Nones 的数量,我更喜欢x, y, z, *_ = s.split() + [None] * 3 - 它的数字就在其中,对于更大的数字来说更易读,更容易参数化和启动时间更短。
  • 如果想摆脱*_,可以写x, y, z = (s.split() + [None]*3)[:3]
  • itertools.chain(s.split(), itertools.repeat(None))
  • @RiaD: 好主意,但是使用x, y, z = chain(s.split(), repeat(None) 你会得到一个ValueError: too many values to unpack,使用x, y, z, *_ = chain(s.split(), repeat(None) 你会尝试在_ 中构建一个无限列表阻塞,直到它耗尽所有可用内存。
  • 啊,你是对的,所以必须添加切片,它变得太长了
【解决方案2】:

extend 在列表末尾添加一个可迭代对象,因此您可以这样做:

l.extend(None for _ in range(3 - len(l))

l.extend([None]*(3-len(l)))

这更优雅一点,但速度稍慢,因为它需要先构造 Nones 列表。

【讨论】:

    【解决方案3】:

    这种应用程序正是itertools 中的padnone 配方的用途(https://docs.python.org/3/library/itertools.html#itertools-recipes):

    x, y, z = itertools.islice(itertools.chain(s.split(), itertools.repeat(None)), 3)
    

    上面还合并了take,只得到3个元素。

    【讨论】:

    • 注意不要在没有islice的情况下运行这个变体:x, y, z, *_ = itertools.chain(s.split(), itertools.repeat(None)),因为它会很高兴地尝试在_中创建一个无限迭代。尽管x, y, z, *_ = itertools.chain(s.split(), itertools.repeat(None, 10)) 提供了重复次数的值
    【解决方案4】:

    这是一个使用itertools.zip_longest 的稍微有趣的解决方案:行为是使用None 来填充较短序列中缺失的元素。

    from itertools import zip_longest
    
    (x, _), (y, _), (z, _) = zip_longest(s.split(), range(3))
    

    与您的原始代码一样,如果 s.split() 包含三个以上的部分,这将引发错误。如果您更愿意默默地丢弃多余的部分,请改为解构为 (x, _), (y, _), (z, _), *_

    【讨论】:

    • 你可以这样做:x,y,z = (k for k, _ in zip_longest(s.split(), range(3)))
    • 是的,我先是这样写的,但后来觉得这样更干净一些。当然是见仁见智。
    【解决方案5】:

    替代方法是使用分区将 z 分配给空白字符串。

    s = "hello there" 
    x, z, y = s.partition(' ')
    

    这种方法切入正题。这样做时,它会将 z 分配给空白而不是 None。

    【讨论】:

      【解决方案6】:

      一个灵活的基于装饰器的解决方案,灵感来自thisthis 答案:

      from itertools import islice, chain, repeat
      
      def variable_return(max_values, default=None):
          def decorator(f):
              def wrapper(*args, **kwargs):
                  return islice(chain(f(*args, **kwargs), repeat(default)), max_values)
              return wrapper
          return decorator
      

      或者毫无意义的浓缩版:

      variable_return = lambda m, d=None: lambda f: lambda *a, **k: islice(chain(f(*a, **k), repeat(d)), m)
      

      可以这样使用:

      @variable_return(3)
      def split(s):
          return s.split()
      
      x, y, z = split(s) # ('hello', 'there', None)
      

      或者像这样:

      x, y, z = variable_return(3)(s.split)() # ('hello', 'there', None)
      

      【讨论】:

        【解决方案7】:

        您可以添加单项列表[None] 所需的次数(编辑:类似于this 但不使用extend):

        l += [None]*(3-len(l))
        

        【讨论】:

          猜你喜欢
          • 2023-03-08
          • 1970-01-01
          • 1970-01-01
          • 2013-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-11
          • 1970-01-01
          相关资源
          最近更新 更多