python: 将"5,4,2,4,1,0" 转换为 [[5, 4], [2, 4], [1, 0]]

8 投票
11 回答
4170 浏览
提问于 2025-04-16 12:19

有没有一种“简单”的方法可以把一个包含数字的字符串转换成一个[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 个回答

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]]
15

一种选择:

>>> 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的回答可能更快,因为它似乎没有进行不必要的迭代。但实际的差异当然还要看列表的大小。

22

在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个元素,这样做会更高效。

撰写回答