¿Cómo convertir la representación de cadena de la lista en una lista?


532

Me preguntaba cuál es la forma más sencilla de convertir una stringlista como la siguiente a list:

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

Incluso en caso de que el usuario ponga espacios entre las comas y espacios dentro de las comillas. Necesito manejar eso también para:

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

en Python

Sé que puedo eliminar espacios con strip()y split()usando el operador dividido y verificar si no hay alfabetos. Pero el código se estaba volviendo muy torpe. ¿Hay alguna función rápida que no conozca?


44
¿Qué estás tratando de lograr realmente? Probablemente hay una manera mucho mejor que tratar de convertir la sintaxis de lista de Python en una lista real ...
Nicholas Knight

1
¿Qué versión de Python estás usando?
Mark Byers

2
@Nicholas Knight: estoy tratando de manejar la entrada del usuario en una aplicación heredada donde todas las listas se ingresaron como listas Unicode con paréntesis cuadrados. @ Marcos Byers, estoy usando Python 2.6 por lo que el método funciona mejor ast.literal
harijay

Respuestas:


769
>>> 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 :

Con ast.literal_eval, puede evaluar de forma segura un nodo de expresión o una cadena que contiene una expresión de Python. La cadena o nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, números, tuplas, listas, dictos, booleanos y Ninguno.


66
Por comentario a continuación, esto es peligroso ya que simplemente ejecuta cualquier python que esté en la cadena. Entonces, si alguien llama para eliminar todo lo que hay allí, felizmente lo hará.
Paul Kenjora

16
@PaulKenjora: Estás pensando eval, no ast.literal_eval.
user2357112 es compatible con Monica

19
ast.literal_evales más seguro que eval, pero en realidad no es seguro . Como explican las versiones recientes de los documentos : "Advertencia Es posible bloquear el intérprete de Python con una cadena lo suficientemente grande / compleja debido a las limitaciones de profundidad de pila en el compilador AST de Python". De hecho, puede ser posible ejecutar código arbitrario a través de un cuidadoso ataque de aplastamiento de la pila, aunque hasta donde yo sé nadie puede construir una prueba pública de concepto para eso.
abarnert

Bueno, pero ¿qué hacer si la lista no tiene citas? ej. [4 de B, 1 de G]
sqp_125

84

El jsonmódulo es una mejor solución siempre que haya una lista de diccionarios en cadena . La json.loads(your_data)función se puede usar para convertirla en una lista.

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

similar

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

Sin embargo, no quiero la lista devuelta en formato Unicode. pero parece que incluso si elimino u '' de la cadena, todavía trata los datos como unicode.
Mansoor Akram

77
Esto funciona para ints pero no para cadenas en mi caso porque cada cadena está entre comillas simples, no entre comillas dobles, suspiro.
Paul Kenjora

44
Según el comentario de @ PaulKenjora, funciona '["a","b"]'pero no para "['a','b']".
Skippy le Grand Gourou

83

El evales peligroso: no debe ejecutar la entrada del usuario.

Si tiene 2.6 o más reciente, use ast en lugar de eval:

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

Una vez que tienes eso, striplas cuerdas.

Si estás en una versión anterior de Python, puedes acercarte mucho a lo que quieres con una simple expresión regular:

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

Esto no es tan bueno como la solución ast, por ejemplo, no maneja correctamente las comillas escapadas en cadenas. Pero es simple, no implica una evaluación peligrosa, y podría ser lo suficientemente bueno para su propósito si está en una Python más antigua sin ast.


¿Podrías decirme por qué dijiste " evalEs peligroso, no debes ejecutar la entrada del usuario"? Estoy usando 3.6
Aaryan Dewan

1
@AaryanDewan si lo usa evaldirectamente, evaluará cualquier expresión de Python válida, que es potencialmente peligrosa. literal_evalresuelve este problema evaluando solo estructuras literales de Python: cadenas, números, tuplas, listas, dictos, booleanos y Ninguno.
Abhishek Menon

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

10

Hay una solución rápida:

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

Los espacios en blanco no deseados en los elementos de la lista se pueden eliminar de esta manera:

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

esto sería aún conservan los espacios dentro de las comillas
tosh

17
Esta es una invitación abierta a la ejecución de código arbitrario, NUNCA haga esto ni nada parecido a menos que sepa con absoluta certeza que la entrada siempre será 100% confiable.
Nicholas Knight

1
Podría usar esta sugerencia porque sabía que mis datos siempre estarían en ese formato y era un trabajo de procesamiento de datos.
Manish Ranjan

9

Inspirado en algunas de las respuestas anteriores que funcionan con paquetes básicos de Python, comparé el rendimiento de algunos (usando Python 3.7.3):

Método 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

Método 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

Método 3: no importar

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

Me decepcionó ver lo que consideraba que el método con la peor legibilidad era el método con el mejor rendimiento ... hay compensaciones a tener en cuenta al elegir la opción más legible ... para el tipo de cargas de trabajo para las que uso Python. valor de lectura sobre una opción ligeramente más eficiente, pero como siempre depende.


9

Si es solo una lista unidimensional, esto se puede hacer sin importar nada:

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

8
Nota de precaución: esto podría ser potencialmente peligroso si alguna de las cadenas dentro de la lista tiene una coma en el medio.
Hassan Kamal

Esto no funcionará si su lista de cadenas es una lista de listas
crypdick

@crypdick Buen punto, agregó una nota al respecto :)
ruohola

6

Asumiendo que todas sus entradas son listas y que las comillas dobles en la entrada en realidad no importan, esto se puede hacer con un simple regexp replace. Es un poco perl-y pero funciona como un encanto. Tenga en cuenta también que el resultado ahora es una lista de cadenas unicode, no especificó que lo necesitaba, pero parece tener sentido dada la entrada 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']

La variable junkers contiene una expresión regular compilada (para la velocidad) de todos los caracteres que no queremos, usando] como carácter requirió algunos trucos de barra invertida. El re.sub reemplaza todos estos caracteres con nada, y dividimos la cadena resultante en las comas.

Tenga en cuenta que esto también elimina espacios de las entradas internas u '["oh no"]' ---> [u'ohno ']. Si esto no es lo que querías, la expresión regular debe mejorarse un poco.


4

Si sabe que sus listas solo contienen cadenas entre comillas, este ejemplo de pyparsing le dará su lista de cadenas despojadas (incluso conservando la Unicode-ness original).

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

Si sus listas pueden tener más tipos de datos, o incluso contener listas dentro de las listas, necesitará una gramática más completa, como esta en el wiki de pyparsing, que manejará tuplas, listas, ints, flotantes y cadenas entre comillas. Funcionará con versiones de Python de nuevo a 2.4


¿Me harías saber cómo usar "parseString (). asList ()", si tengo este tipo de cadena: '["A", "B", "C", ["D"]]', como tú he dicho que el pyparsing también puede hacer eso. pero no parece haber encontrado la manera correcta de hacerlo.
Mansoor Akram

"Si sus listas pueden tener más tipos de datos, o incluso contener listas dentro de las listas, necesitará una gramática más completa". Consulte el enlace que proporcioné en mi respuesta para obtener un analizador que manejará las listas anidadas y varios otros tipos de datos.
PaulMcG

Pyparsing ya no está alojado en wikispaces. El parsePythonValue.pyejemplo ahora está en GitHub en github.com/pyparsing/pyparsing/blob/master/examples/…
PaulMcG

1

Para completar aún más la respuesta de @Ryan usando json, una función muy conveniente para convertir Unicode es la publicada aquí: https://stackoverflow.com/a/13105359/7599285

ex con comillas dobles o simples:

>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

Me gustaría proporcionar una solución de patrones más intuitiva con regex. La siguiente función toma como entrada una lista en cadena que contiene cadenas arbitrarias.

Explicación por pasos: elimina todos los espacios en blanco, los corchetes y los separadores de valores (siempre que no sean parte de los valores que desea extraer, de lo contrario, hacen que la expresión regular sea más compleja). Luego divide la cadena limpia entre comillas simples o dobles y toma los valores no vacíos (o valores indexados impares, sea cual sea la preferencia).

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

muestra de prueba : "['21'," foo "'6', '0'," A "]"


0

y con Python puro: no importar ninguna biblioteca

[x for x in  x.split('[')[1].split(']')[0].split('"')[1:-1] if x not in[',',' , ',', ']]

0

Puede encontrarse con este problema al tratar con datos raspados almacenados como Pandas DataFrame.

Esta solución funciona como un encanto si la lista de valores está presente como texto .

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

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

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

No se requiere biblioteca externa.


-1

Entonces, siguiendo todas las respuestas, decidí cronometrar los métodos más comunes:

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

¡Así que al final regex gana!


-1

puede guardarse el .strip () fcn simplemente cortando el primer y el último carácter de la representación de cadena de la lista (consulte la tercera línea a continuación)

>>> 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'>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.