如何将列表的字符串表示形式转换为列表?


530

我想知道最简单的方法是将string类似以下的列表转换为list

x = u'[ "A","B","C" , " D"]'

即使用户在逗号之间加上空格,也要在引号内使用空格。我还需要处理以下内容:

x = ["A", "B", "C", "D"] 

在Python中。

我知道我可以使用strip()split()使用split运算符删除空格,并检查非字母。但是代码变得非常混乱。有我不知道的快速功能吗?


4
您实际上想完成什么?比尝试将Python列表语法转换为实际列表要好得多……
Nicholas Knight

1
您正在使用哪个版本的Python?
Mark Byers

2
@Nicholas Knight:我正在尝试处理旧版应用程序中的用户输入,在该应用程序中,所有列表都被输入为带有方括号的Unicode列表。@Mark Byers,我使用的是python 2.6,因此ast.literal方法效果最好
harijay 2009年

Answers:


768
>>> import ast
>>> x = u'[ "A","B","C" , " D"]'
>>> x = ast.literal_eval(x)
>>> x
['A', 'B', 'C', ' D']
>>> x = [n.strip() for n in x]
>>> x
['A', 'B', 'C', 'D']

ast.literal_eval

使用ast.literal_eval,您可以安全地评估表达式节点或包含Python表达式的字符串。提供的字符串或节点只能由以下Python文字结构组成:字符串,数字,元组,列表,字典,布尔值和无。


6
根据下面的注释,这很危险,因为它仅运行字符串中的任何python。因此,如果有人打电话删除其中的所有内容,它会很乐意。
Paul Kenjora '17

16
@PaulKenjora:您在想eval,不是ast.literal_eval
user2357112支持Monica

19
ast.literal_eval更安全eval,但它不是真正的安全。正如文档的最新版本所解释的:“警告由于Python的AST编译器的堆栈深度限制,使用足够大/复杂的字符串可能会使Python解释器崩溃。” 实际上,通过仔细的堆栈粉碎攻击可能可以运行任意代码,尽管据我所知,没有人为此构建公开的概念证明。
abarnert

好吧,但是如果列表没有引号怎么办?例如[B的4,G的1]
sqp_125

84

json每当有字典的字符串列表时,该模块都是更好的解决方案。该json.loads(your_data)函数可用于将其转换为列表。

>>> import json
>>> x = u'[ "A","B","C" , " D"]'
>>> json.loads(x)
[u'A', u'B', u'C', u' D']

相似地

>>> x = u'[ "A","B","C" , {"D":"E"}]'
>>> json.loads(x)
[u'A', u'B', u'C', {u'D': u'E'}]

但是我不希望以Unicode格式返回列表。但是似乎即使我从字符串中删除u''仍然会将数据视为unicode。
曼苏尔·阿克拉姆

7
在我的情况下,这适用于整数,但不适用于字符串,因为每个字符串都是单引号而不是双引号。
Paul Kenjora '17

4
根据@PaulKenjora的评论,它适用于,'["a","b"]'但不适用于"['a','b']"
Skippy le Grand Gourou

83

eval很危险-您不应该执行用户输入。

如果您使用2.6或更高版本,请使用ast而不是eval:

>>> import ast
>>> ast.literal_eval('["A","B" ,"C" ," D"]')
["A", "B", "C", " D"]

一旦有了,就可以strip了。

如果您使用的是旧版Python,则可以使用简单的正则表达式非常接近所需的内容:

>>> x='[  "A",  " B", "C","D "]'
>>> re.findall(r'"\s*([^"]*?)\s*"', x)
['A', 'B', 'C', 'D']

这不如ast解决方案好,例如,它不能正确处理字符串中的转义引号。但这很简单,不涉及危险的评估,如果您使用的是没有ast的旧Python,则可能足以满足您的目的。


您能否告诉我为什么说“这eval很危险-您不应该执行用户输入”。我正在使用3.6
Aaryan Dewan

1
如果eval直接使用@AaryanDewan ,它将评估任何有效的python表达式,这有潜在的危险。literal_eval仅评估Python文字结构即可解决此问题:字符串,数字,元组,列表,字典,布尔值和无。
Abhishek Menon

14
import ast
l = ast.literal_eval('[ "A","B","C" , " D"]')
l = [i.strip() for i in l]

10

有一个快速的解决方案:

x = eval('[ "A","B","C" , " D"]')

可以通过以下方式删除列表元素中不需要的空格:

x = [x.strip() for x in eval('[ "A","B","C" , " D"]')]

这样仍然可以保留引号内的空格
tosh

17
这是对任意代码执行的公开邀请,除非您绝对确定输入将始终是100%可信的,否则请勿执行此操作或类似操作。
尼古拉斯·奈特

1
我可以使用此建议,因为我知道我的数据将始终采用这种格式,并且是数据处理工作。
Manish Ranjan

9

从上面适用于基本python软件包的一些答案的启发中,我比较了一些(使用Python 3.7.3)的性能:

方法1:AST

import ast
list(map(str.strip, ast.literal_eval(u'[ "A","B","C" , " D"]')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, ast.literal_eval(u'[ \"A\",\"B\",\"C\" , \" D\"]')))", setup='import ast', number=100000)
# 1.292875313000195

方法2:JSON

import json
list(map(str.strip, json.loads(u'[ "A","B","C" , " D"]')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, json.loads(u'[ \"A\",\"B\",\"C\" , \" D\"]')))", setup='import json', number=100000)
# 0.27833264000014424

方法3:不导入

list(map(str.strip, u'[ "A","B","C" , " D"]'.strip('][').replace('"', '').split(',')))
# ['A', 'B', 'C', 'D']

import timeit
timeit.timeit(stmt="list(map(str.strip, u'[ \"A\",\"B\",\"C\" , \" D\"]'.strip('][').replace('\"', '').split(',')))", number=100000)
# 0.12935059100027502

我很失望地看到我认为可读性最差的方法是性能最好的方法。选择可读性最高的选项时要权衡考虑...对于我通常使用python的工作量类型相对于性能稍高的选项,它更重视可读性,但通常情况下,它取决于。


9

如果只是一维列表,则无需导入任何内容即可完成:

>>> x = u'[ "A","B","C" , " D"]'
>>> ls = x.strip('[]').replace('"', '').replace(' ', '').split(',')
>>> ls
['A', 'B', 'C', 'D']

8
警告说明:如果列表中的任何字符串之间有逗号,则可能有潜在的危险。
哈桑·卡玛尔

如果您的字符串列表是列表列表,则此方法将不起作用
crypdick

@crypdick好点,并补充说明:)
ruohola

6

假设所有输入都是列表,并且输入中的双引号实际上并不重要,则可以使用简单的regexp替换来完成。它有点Perl-y,但是却像魅力一样。还要注意,输出现在是unicode字符串的列表,您没有指定所需的字符串,但是对于unicode输入,这似乎很有意义。

import re
x = u'[ "A","B","C" , " D"]'
junkers = re.compile('[[" \]]')
result = junkers.sub('', x).split(',')
print result
--->  [u'A', u'B', u'C', u'D']

junkers变量包含一个我们不想使用的所有字符的正则表达式(用于速度),使用]作为字符需要一些反斜杠技巧。re.sub将所有这些字符全部替换为空,然后将结果字符串拆分为逗号。

请注意,这还会从内部条目u'[“ oh no”]'---> [u'ohno']中删除空格。如果这不是您想要的,则需要增加正则表达式。


4

如果您知道列表仅包含带引号的字符串,则此pyparsing示例将为您提供剥离字符串的列表(甚至保留原始Unicode-ness)。

>>> from pyparsing import *
>>> x =u'[ "A","B","C" , " D"]'
>>> LBR,RBR = map(Suppress,"[]")
>>> qs = quotedString.setParseAction(removeQuotes, lambda t: t[0].strip())
>>> qsList = LBR + delimitedList(qs) + RBR
>>> print qsList.parseString(x).asList()
[u'A', u'B', u'C', u'D']

如果你的列表可以有更多的数据类型,甚至包含列表中列出,那么你将需要一个更完整的语法-像这样一个在pyparsing wiki,它可以处理的元组,列表,整数,浮点数,和引用字符串。将适用于2.4之前的Python版本。


如果我有这种字符串,请问我如何使用“ parseString()。asList()”:'[“ A”,“ B”,“ C”,[“ D”]]'声称pyparsing也可以做到这一点。但是o似乎没有找到正确的方法。
Mansoor Akram

“如果列表可以具有更多的数据类型,或者甚至包含列表中的列表,那么您将需要一个更完整的语法”-请参阅我在答案中提供的链接,其中提供了用于处理嵌套列表以及各种其他数据类型的解析器。
PaulMcG

Pyparsing不再在Wikispace上托管。该parsePythonValue.py示例现在位于GitHub上的github.com/pyparsing/pyparsing/blob/master/examples/…–
PaulMcG,

1

为了进一步使用json完成@Ryan的答案,这里发布的一个非常方便的函数来转换unicode: https

例如,用双引号或单引号引起来:

>print byteify(json.loads(u'[ "A","B","C" , " D"]')
>print byteify(json.loads(u"[ 'A','B','C' , ' D']".replace('\'','"')))
['A', 'B', 'C', ' D']
['A', 'B', 'C', ' D']

0

我想用正则表达式提供一个更直观的模式解决方案。下面的函数将包含任意字符串的字符串化列表作为输入。

分步说明: 删除所有whitespacing,花括号和value_separators(前提是它们不是要提取的值的一部分,否则会使正则表达式更复杂)。然后,将清洗后的字符串用单引号或双引号引起来,并采用非空值(或奇数索引值,无论使用哪种首选项)。

def parse_strlist(sl):
import re
clean = re.sub("[\[\],\s]","",sl)
splitted = re.split("[\'\"]",clean)
values_only = [s for s in splitted if s != '']
return values_only

testsample:“ ['21',” foo“'6','0',” A“]”



0

在处理存储为Pandas DataFrame的抓取数据时,您可能会遇到这样的问题。

如果值列表以文本形式出现,则此解决方案的工作方式类似于魅力。

def textToList(hashtags):
    return hashtags.strip('[]').replace('\'', '').replace(' ', '').split(',')

hashtags = "[ 'A','B','C' , ' D']"
hashtags = textToList(hashtags)

Output: ['A', 'B', 'C', 'D']

无需外部库。


-1

因此,按照所有答案,我决定为最常见的方法计时:

from time import time
import re
import json


my_str = str(list(range(19)))
print(my_str)

reps = 100000

start = time()
for i in range(0, reps):
    re.findall("\w+", my_str)
print("Regex method:\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    json.loads(my_str)
print("json method:\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    ast.literal_eval(my_str)
print("ast method:\t\t", (time() - start) / reps)

start = time()
for i in range(0, reps):
    [n.strip() for n in my_str]
print("strip method:\t", (time() - start) / reps)



    regex method:    6.391477584838867e-07
    json method:     2.535374164581299e-06
    ast method:      2.4425282478332518e-05
    strip method:    4.983267784118653e-06

因此,最终正则表达式获胜!


-1

您可以通过从列表的字符串表示中切下第一个和最后一个字符来节省.strip()fcn(请参见下面的第三行)

>>> mylist=[1,2,3,4,5,'baloney','alfalfa']
>>> strlist=str(mylist)
['1', ' 2', ' 3', ' 4', ' 5', " 'baloney'", " 'alfalfa'"]
>>> mylistfromstring=(strlist[1:-1].split(', '))
>>> mylistfromstring[3]
'4'
>>> for entry in mylistfromstring:
...     print(entry)
...     type(entry)
... 
1
<class 'str'>
2
<class 'str'>
3
<class 'str'>
4
<class 'str'>
5
<class 'str'>
'baloney'
<class 'str'>
'alfalfa'
<class 'str'>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.