【问题标题】:Most pythonic way to extend a list to exactly a certain length? [duplicate]将列表扩展到特定长度的最 Pythonic 方式? [复制]
【发布时间】:2014-06-05 17:54:56
【问题描述】:

我的目标是获取一个未知数量的元素列表,并将其扩展/切片为精确的 n 元素,用0 填充太短的列表并切片太长的列表。

例如

n = 10
foo = [1,2,3,4]
print some_func(foo,n) 

应该返回[1,2,3,4,0,0,0,0,0,0],并且

n = 10
foo = [1,2,3,4,5,6,7,8,9,10,11,12]
print some_func(foo,n)

应该返回[1,2,3,4,5,6,7,8,9,10]


现在我正在这样做:

def some_function(l, n):
    l.extend([0] * n)
    l = l[:n]
    return l

但这似乎效率低下。有没有更 Pythonic 的方式来做到这一点?


编辑:澄清一点,我不是要修改原始数组,而是返回一个可以是浅拷贝的新数组。

【问题讨论】:

  • 为什么看起来效率低?
  • l = l[:n] 不会修改原始列表,只是创建一个新的临时列表,当some_function 返回时,其唯一引用超出范围。
  • @shx2 这是 pad 和 slice,我想知道是否有更有效的方法来做这两者。
  • 对不起,输入问题时忘记return l

标签: python


【解决方案1】:

执行此操作的唯一可能“更 Pythonic”的方式几乎就是您拥有的方式,但跳过额外的变量分配并直接返回结果。您还可以绝对通过 conforming to PEP8 使用您的函数和变量名称使其更具 Pythonic。

除此之外,您可以通过只添加所需数量的零来提高效率,而不是构建一个太长的列表然后对其进行修剪。

def pad_or_truncate(some_list, target_len):
    return some_list[:target_len] + [0]*(target_len - len(some_list))

分解一下,这里有两种情况(不考虑输入已经完全正确长度的琐碎情况)。要么列表太长,要么列表太短。

如果列表太长,我们就对其进行切片。 some_list[:target_len] 负责这一点。因为切片操作是友好的,所以如果目标长度超出列表的实际长度,这不会爆炸。

如果列表太短,我们用零填充它。我选择通过乘以一个列表文字1 来做到这一点,但如果这更像是你的一杯茶,你可以以完全相同的方式使用列表推导式2。只需确定要添加多少个零(零,如果列表不太短,或target_len - len(some_list)),然后连接由那么多零组成的列表。大功告成!

如果您想让它成为就地操作(因为您的原始示例似乎是尝试但未能实现;请参阅@chepner 的评论),您只需将 return <...> 更改为 @ 987654326@.


1一些简短的timeit 结果表明,文字乘法比列表推导所暗示的双重迭代要快一些。

2对于后代,列表理解版本看起来像:

return some_list[:target_len] + [0 for _ in range(target_len - len(some_list))]

【讨论】:

  • 谢谢,这正是我想要的。
【解决方案2】:

islice-ing 使用填充生成器连接原始列表怎么样?

from itertools import islice, repeat
def fit_to_size(l, n):
    return list(islice(
        ( x
          for itr in (l, repeat(0))
          for x in itr ),
        n))

您可能更喜欢这种稍微更明确的实现:

def fit_to_size(l, n):
    def gen():
        yield from l
        while True: yield 0
    return list(islice(gen(), n))

【讨论】:

    【解决方案3】:

    建立比你需要的更大的列表是非常低效的(特别是如果n 变得很大);相反,只添加您需要的填充。此外,如果函数就地更改其参数,则 Python 约定 return None

    def some_func(l, n, pad=0):
        if len(l) >= n:
            del l[n:]
        else:
            l.extend([pad] * (n - len(l)))
    

    例子:

    >>> l = [1, 2, 3, 4, 5]
    >>> some_func(l, 3)
    >>> l
    [1, 2, 3]
    >>> some_func(l, 5)
    >>> l
    [1, 2, 3, 0, 0]
    

    或者,return 一个新列表:

    def some_func(l, n, pad=0):
        if len(l) >= n:
            return l[:n]
        return l + ([pad] * (n - len(l)))
    

    【讨论】:

      【解决方案4】:

      为什么不使用条件逻辑?

      def pad_or_slice(L, n):
          if len(L) < n:
              return L + ([0] * (n - len(L)))
          else:
              return L[:n]
      

      这样你只做两者之一,而不是两者,代价是检查列表的长度,这不应该太昂贵。

      【讨论】:

        【解决方案5】:

        它的效率并没有明显提高,但我会做l = (l + [0] * (n - len(l)))[:n]。所以你的some_function 看起来像这样

        def some_function(list, n):
            return (list + [0] * (n - len(list)))[:n]
        

        【讨论】:

        • 这不会对太长的列表进行切片。
        【解决方案6】:

        为了正确修改原始列表,您需要从对象中删除尾随切片。

        def some_function(l, n):
            l.extend([0] * n)
            del l[n:]
        

        【讨论】:

        • 第二个不切片。
        • 我跳过了截断过长列表的要求,所以我只删除了不处理这种情况的两个选项。
        猜你喜欢
        • 1970-01-01
        • 2021-11-22
        • 1970-01-01
        • 1970-01-01
        • 2016-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多