用另一个数组填充二维数组的一维。

3

i have two lists:

a=[[10, 0], [12,1], [13, 8], [2, -3]]
b=[1, 2, -30, 404]

我希望用b的值替换a[*][1],这样我的结果将如下所示:
[[10, 1], [12, 2], [13, -30], [2, 404]]

一个显而易见的方式(对于我这样的真正的C程序员)可能是:

for i in range(len(a)):
   a[i][1]=b[i]

但是这种方法感觉不太符合 Python 的风格。

那么在 Python 的风格下应该如何做呢?

另外,b 列表的元素数量可能比 a 多或少。如果少的话,a 中剩余的元素应该保持不变。

a=[[10, 0], [12,1], [13, 8], [2, -3]]
b=[10, 20]

result=[[10, 10], [12,20], [13, 8], [2, -3]]

如果它们的数量更多,我希望添加新的条目,并设置默认的第一个元素(例如 None )。
a=[[10, 0], [12,1]]
b=[100, 200, -30, 404]

result=[[10, 100], [12,200], [None, -30], [None, 404]]

这能用列表推导式实现吗?

能否告诉我你为什么认为你的解决方案不符合 Pythonic 的风格,这会帮助我更好地理解。因为正如我在答案中所说的那样,我并不认为它不符合 Pythonic 风格——简单的列表推导式固然美妙,但“稀疏胜于密集”才是更符合 Pythonic 风格的写法。 - kojiro
@kojiro for(i=0; i<len(); i++) 总是让我觉得我在太过于思考 C 了。 - umläute
3个回答

2

使用zip和列表推导:

>>> a[:] = [[x, z] for (x, y), z   in zip(a, b)]
>>> a
[[10, 1], [12, 2], [13, -30], [2, 404]]

对于长度不相等的列表,您可以使用 iterools.izip_longest

>>> from itertools import izip_longest
>>> a = [[10, 0], [12,1], [13, 8], [2, -3]]
>>> b = [10, 20]
>>> sen = object()
#len(a) > len(b)
>>> a[:] = [[x, y if z is sen else z]
                          for (x, y), z in izip_longest(a, b, fillvalue=sen)]
>>> a
[[10, 10], [12, 20], [13, 8], [2, -3]]

>>> a = [[10, 0], [12,1]]
>>> b = [100, 200, -30, 404]
#len(b) > len(a)
>>> a[:] = [[None if x is sen else x , z] 
                          for (x, y), z in izip_longest(a, b, fillvalue=[sen]*2)]
>>> a
[[10, 100], [12, 200], [None, -30], [None, 404]]

如果您认为列表推导式过于难以阅读,请使用 生成器函数

def solve(a, b):
    sen = object()
    if len(a) == len(b):
        for (x, y), z in zip(a, b):
            yield [x, z]
    elif len(a) > len(b):
        for (x, y), z in izip_longest(a, b, fillvalue=sen):
            yield [x, y if z is sen else z]
    else:
        for (x, y), z in izip_longest(a, b, fillvalue=[sen]*2):
            yield [None if x is sen else x , z]

演示:

>>> list(solve([[10, 0], [12,1], [13, 8], [2, -3]], [1, 2, -30, 404]))
[[10, 1], [12, 2], [13, -30], [2, 404]]

>>> list(solve([[10, 0], [12,1], [13, 8], [2, -3]], [10, 20]))
[[10, 10], [12, 20], [13, 8], [2, -3]]

>>> list(solve([[10, 0], [12,1]], [100, 200, -30, 404]))
[[10, 100], [12, 200], [None, -30], [None, 404]]

1
可爱,但它真的比简单循环更好吗? - kojiro
@kojiro 列表生成式和无索引。 - Ashwini Chaudhary
我们可以就pythonic的含义展开争论,但我认为这违反了python -m this | cat -n中的第5、9、15行,可能还有第19行。 - kojiro

2

enumerate通常被认为是Python中循环列表索引范围的一种替代方法。除此之外,你的解决方案并没有什么不符合Python风格的地方。

for i, v in enumerate(b):
  try:
    a[i][1] = v
  except IndexError:
    a.append([None, v])

或者,使用迭代器避免多个try/catch触发器:
bnumer = enumerate(b)
for i, v in bnumer:
  try:
    a[i][1] = v
  except IndexError:
    break

# extend the list with the remaining default values from b
a.extend([None,v] for v in bnumer)

我喜欢那个(尤其是非优化的第一个解决方案); 但是a[i]=[None, v]会因为索引错误而退出; a[i]+=[[None, v]] - umläute
虽然在简单情况下(两个列表长度相等)我最喜欢@hcwhsa的答案,但这个解决方案似乎对于两种情况都更易读。 - umläute

1

尝试使用列表推导式实现:

if len(a) > len(b):
    result = [[e[0], b[i]] if i < len(b) else e for i, e in enumerate(a)]
else:
    result = [[a[i][0], e] if i < len(a) else [None, e] for i, e in enumerate(b)]

上述内容考虑了问题中描述的三种情况,是否更符合Python风格由您来判断 - 但它会产生正确的结果,这才是最重要的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接