在每行中乘以2个元素,创建新列,列表的列表 - python

1 投票
2 回答
1148 浏览
提问于 2025-04-18 18:00

我有一个列表,里面又包含了很多列表:

mylist = [['Bob','AA',3,2.1,4],['Sally','BB',2,5.1,3],['Jim','CC',2,1.5,4]]

我需要把第2个和第3个元素相乘,然后把结果放到第4个位置,这样其他的元素就会往后移动。同时,我还想把浮点数的误差格式化一下。想要的输出结果是:

mylist = [['Bob','AA',3,2.1,6.3,4],['Sally','BB',2,5.1,10.2,3],['Jim','CC',2,1.5,3,4]]

我几乎已经用以下方法解决了这个问题:

for i, val in enumerate(mylist):
    sol = (float(val[2]*val[3]))
    val.insert(4,'%.3f' % sol)

print(mylist)

我得到的结果是:

[['Bob', 'AA', 3, 2.1, 6.300, 4], ['Sally', 'BB', 2, 5.1, 10.200, 3], ['Jim', 'CC', 2, 1.5, 3.000, 4]]

我有两个问题:

  1. 有没有比我现在的方法更符合Python风格的做法?(如果能用列表推导式就更好了)
  2. 我该如何正确格式化浮点数的误差?(编辑:在插入时加上了'%.3f' %,看起来效果还不错。)

2 个回答

-1
  1. 有没有比我现在的解决方案更符合Python风格的方法?(如果能用列表推导就更好了)

你用for循环的方法是对的,而且很“Python风格”。这里有个小提示:

  • 如果你在代码中不使用索引i,那么就不需要用enumerate()

话虽如此,如果你想尝试其他更符合Python风格的替代方案,也可以用列表推导。这里有两种方法:

使用运算符重载的列表推导:

你可以通过重载+运算符来实现列表的连接。如果你放宽条件,不强求新产品放在第4个索引,而是放在列表的末尾,这样就可以用一个列表推导来替代for循环:

myList = [ val + [float( format( val[2]*val[3], '.3f'))] for val in myList ]

这样我们就能得到:

>>> print(myList)
[['Bob', 'AA', 3, 2.1, 4, 6.3], ['Sally', 'BB', 2, 5.1, 3, 10.2], ['Jim', 'CC', 2, 1.5, 4, 3.0]]

如果你还是想把产品放在第4个索引,可以用一种稍微不那么优雅的写法来实现:

myList = [ val[:3] + [float( format( val[2]*val[3], '.3f'))] + val[4:] for val in myList ]

使用insert()的列表推导:

insert()函数的工作方式有点不同,所以要小心。insert()在列表推导中使用时,会修改原始列表。因此,你不需要把列表推导的结果赋值给一个新变量。

考虑到这一点,你可以用以下方式替代for循环:

[ val.insert( 4, float( format( val[2] * val[3], '.3f') ) for val in myList ];

这将正确更新你的myList变量,结果和之前一样:

>>> print(myList)
[['Bob', 'AA', 3, 2.1, 6.3, 4], ['Sally', 'BB', 2, 5.1, 10.2, 3], ['Jim', 'CC', 2, 1.5, 3.0, 4]]

关于使用insert()函数的列表推导的注意事项:

  1. 在列表推导的末尾加上分号;是必要的,这样可以抑制不必要的输出,否则会显示[None, None, None]
  2. 不过,在这种情况下使用列表推导可能不太推荐,因为这样会让代码不太容易阅读,并且浪费内存资源,因为生成了一个从未使用的列表。
2. How do I correctly format the float errors? (EDIT: Included '%.3f' % at the insert, seems to work ok.)

如你所见,format()函数在这里效果最好。

关于浮点数精度的注意事项:

  1. 使用format()函数可以为数字分配适当的浮点数精度,之后将结果字符串转换为float()可以去掉多余的尾零。
  2. 因为你在计算一个乘积,所以不需要把其中一个元素或乘积本身转换为float(),因为在乘法过程中不会丢失精度。不过,如果你要对两个列表元素进行除法运算,就必须先把除数或被除数转换为float( val[2] ),以确保进行浮点数除法,否则会变成整数除法。
1

有没有比我现在的解决方案更符合Python风格的方法?(如果能用列表推导就更好了)

列表推导是一种在Python中创建列表的好方法。但如果你只是想对一堆值进行操作,使用普通的for循环会更合适,就像你现在做的那样。

你的代码其实没有什么大问题,但和大多数代码一样,总是有改进的空间:

  • 如果你不需要i这个值,就不要用enumerate,直接遍历列表就可以了。
  • 不要把那些已经是float类型的值再转换成float
  • 除非为了提高可读性,否则不要在整个表达式周围加多余的括号。
  • 格式化单个值时,format函数通常比str.format%更简单、更易读(虽然效率可能差不多,但这不太重要)。
  • 变量名应该尽量使用完整的英文单词,只有在标准技术缩写的情况下可以例外。
  • 在乘两个复杂表达式时,最好在*周围加空格,这样会更清晰(虽然value[2]在这里有点边缘)。

所以:

for value in mylist:
    solution = value[2] * value[3]
    value.insert(4, format(solution, '.3'))

PEP 8对这些问题提供了一些指导,但不多。


同时,这些元素可能有其固有的意义。我不知道它们代表什么,但把它们拆分成命名变量可能会更好,而不是直接使用索引:

    name, thingy, count, weight, stuffness = value
    solution = count * weight

或者:

    _, _, count, weight, _ = value
    solution = count * weight

此外,考虑到这些东西是固定形状的异构集合,可能把它们存储为元组而不是列表会更好。当然,你现在是在修改列表,所以这不适用。但也许你根本不应该这样修改。也许更好的做法是构建一个新的元组列表。在这种情况下,你实际上可以使用列表推导:

[value + (format(value[2] * value[3], '.3'),) for value in mylist]

…或者,结合最后两个想法:

[(name, thingy, count, weight, stuffness, format(count * weight, '.3'))
 for (name, thingy, count, weight, stiffness) in mylist]

这是否真的更易读或更符合Python风格,还是有争议的。

撰写回答