在每行中乘以2个元素,创建新列,列表的列表 - python
我有一个列表,里面又包含了很多列表:
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]]
我有两个问题:
- 有没有比我现在的方法更符合Python风格的做法?(如果能用列表推导式就更好了)
- 我该如何正确格式化浮点数的误差?(编辑:在插入时加上了'%.3f' %,看起来效果还不错。)
2 个回答
- 有没有比我现在的解决方案更符合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()
函数的列表推导的注意事项:
- 在列表推导的末尾加上分号
;
是必要的,这样可以抑制不必要的输出,否则会显示[None, None, None]
。 - 不过,在这种情况下使用列表推导可能不太推荐,因为这样会让代码不太容易阅读,并且浪费内存资源,因为生成了一个从未使用的列表。
2. How do I correctly format the float errors? (EDIT: Included '%.3f' % at the insert, seems to work ok.)
如你所见,format()
函数在这里效果最好。
关于浮点数精度的注意事项:
- 使用
format()
函数可以为数字分配适当的浮点数精度,之后将结果字符串转换为float()
可以去掉多余的尾零。 - 因为你在计算一个乘积,所以不需要把其中一个元素或乘积本身转换为
float()
,因为在乘法过程中不会丢失精度。不过,如果你要对两个列表元素进行除法运算,就必须先把除数或被除数转换为float( val[2] )
,以确保进行浮点数除法,否则会变成整数除法。
有没有比我现在的解决方案更符合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风格,还是有争议的。