python: 将"5,4,2,4,1,0" 转换为 [[5, 4], [2, 4], [1, 0]]
有没有一种“简单”的方法可以把一个包含数字的字符串转换成一个[x,y]的整数列表呢?
# from: '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
# to: [[5, 4], [2, 4], [1, 0], [3, 0], [5, 1], [3, 3], [14, 32], [3, 5]]
顺便说一下,下面的方法可以实现这个功能,但我不觉得它简单……另外,可以假设输入的字符串已经过验证,确保它只包含偶数个数字,并且这些数字之间用逗号隔开。
num_str = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
numpairs_lst = [] # ends up as [[5, 4], [2, 4], [1, 0], ...]
current_num_str = '' # the current num within the str; stop when a comma is found
xy_pair = [] # this is one of the [x,y] pairs -> [5, 4]
for ix,c in enumerate(num_str):
if c == ',':
xy_pair.append(int(current_num_str))
current_num_str = ''
if len(xy_pair) == 2:
numpairs_lst.append(xy_pair)
xy_pair = []
else:
current_num_str += c
# and, take care of last number...
xy_pair.append(int(current_num_str))
numpairs_lst.append(xy_pair)
11 个回答
#!/usr/bin/env python
from itertools import izip
def pairwise(iterable):
"s -> (s0,s1), (s2,s3), (s4, s5), ..."
a = iter(iterable)
return izip(a, a)
s = '5,4,2,4,1,0,3,0,5,1,3,3,4,3,3,5'
fields = s.split(',')
print [[int(x), int(y)] for x,y in pairwise(fields)]
这段内容摘自@martineau的回答,是针对我提问的,我发现这个方法非常快。
输出结果:
[[5, 4], [2, 4], [1, 0], [3, 0], [5, 1], [3, 3], [4, 3], [3, 5]]
一种选择:
>>> num_str = '5,4,2,4,1,0,3,0,5,1,3,3,4,3,3,5'
>>> l = num_str.split(',')
>>> zip(l[::2], l[1::2])
[('5', '4'), ('2', '4'), ('1', '0'), ('3', '0'), ('5', '1'), ('3', '3'), ('4', '3'), ('3', '5')]
参考资料:str.split()
、zip()
、关于序列类型和切片的一般信息
如果你真的想要整数,可以先用 map
把列表转换成整数:
>>> l = map(int, num_str.split(','))
解释:
split
会把字符串分割成单个元素的列表。这里的技巧在于切片:语法是 list[start:end:step]
。l[::2]
会返回从第一个元素开始的每隔一个元素(也就是第一个、第三个……),而第二个切片 l[1::2]
则是从第二个元素开始的每隔一个元素(也就是第二个、第四个……)。
更新:如果你真的想要列表,可以在结果列表上再次使用 map
:
>>> xy_list = map(list, xy_list)
注意,@Johnsyweb的回答可能更快,因为它似乎没有进行不必要的迭代。但实际的差异当然还要看列表的大小。
在Python中,有两个很重要的一行代码的写法,可以让事情变得“简单明了”。
第一个写法是使用 zip()。根据Python文档的说明:
可迭代对象的从左到右的评估顺序是有保证的。这使得我们可以用 zip(*[iter(s)]*n) 的方式把数据系列分成n个长度的组。
那么应用到你的例子中:
>>> num_str = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
>>> zip(*[iter(num_str.split(","))]*2)
[('5', '4'), ('2', '4'), ('1', '0'), ('3', '0'), ('5', '1'),
('3', '3'), ('14', '32'), ('3', '5')]
这样会生成每个长度为2的元组。
如果你想让子元素的长度不同:
>>> zip(*[iter(num_str.split(","))]*4)
[('5', '4', '2', '4'), ('1', '0', '3', '0'), ('5', '1', '3', '3'),
('14', '32', '3', '5')]
第二个写法是 列表推导式。如果你想让子元素变成列表,可以用推导式包裹起来:
>>> [list(t) for t in zip(*[iter(num_str.split(","))]*4)]
[['5', '4', '2', '4'], ['1', '0', '3', '0'], ['5', '1', '3', '3'],
['14', '32', '3', '5']]
>>> [list(t) for t in zip(*[iter(num_str.split(","))]*2)]
[['5', '4'], ['2', '4'], ['1', '0'], ['3', '0'], ['5', '1'], ['3', '3'],
['14', '32'], ['3', '5']]
任何不完整的子元素组会被 zip() 截断。所以如果你的字符串不是2的倍数,比如说,你会丢失最后一个元素。
如果你想返回不完整的子元素(也就是说,如果你的 num_str
不是子元素长度的倍数),可以使用 切片写法:
>>> l=num_str.split(',')
>>> [l[i:i+2] for i in range(0,len(l),2)]
[['5', '4'], ['2', '4'], ['1', '0'], ['3', '0'], ['5', '1'],
['3', '3'], ['14', '32'], ['3', '5']]
>>> [l[i:i+7] for i in range(0,len(l),7)]
[['5', '4', '2', '4', '1', '0', '3'], ['0', '5', '1', '3', '3', '14', '32'],
['3', '5']]
如果你想让每个元素都是整数,可以在进行其他转换之前先应用这个:
>>> nums=[int(x) for x in num_str.split(",")]
>>> zip(*[iter(nums)]*2)
# etc etc etc
正如评论中提到的,从Python 2.4开始,你还可以用 生成器表达式 替代列表推导式,只需把 [ ]
换成 ( )
,像这样:
>>> nums=(int(x) for x in num_str.split(","))
>>> zip(nums,nums)
[(5, 4), (2, 4), (1, 0), (3, 0), (5, 1), (3, 3), (14, 32), (3, 5)]
# or map(list,zip(nums,nums)) for the list of lists version...
如果你的字符串很长,而你知道你只需要2个元素,这样做会更高效。