Come convertire la rappresentazione in formato stringa dell'elenco in un elenco?


532

Mi chiedevo quale sia il modo più semplice per convertire un stringelenco come il seguente in un list:

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

Anche nel caso in cui l'utente inserisca spazi tra le virgole e spazi all'interno delle virgolette. Devo gestirlo anche per:

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

in Python.

So di poter rimuovere gli spazi con strip()e split()utilizzando l'operatore di divisione e verificare la presenza di non alfabeti. Ma il codice stava diventando molto kludgy. Esiste una funzione rapida di cui non sono a conoscenza?


4
Cosa stai effettivamente cercando di realizzare? Probabilmente c'è un modo molto migliore di provare a convertire la sintassi dell'elenco Python in un elenco reale ...
Nicholas Knight,

1
Quale versione di Python stai usando?
Mark Byers,

2
@Nicholas Knight: sto cercando di gestire l'input dell'utente in un'app legacy in cui tutti gli elenchi sono stati inseriti come elenchi Unicode con parentesi quadrata. @Mark Byers, sto usando Python 2.6, quindi l'approccio ast.literal funziona meglio
harijay,

Risposte:


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, puoi tranquillamente valutare un nodo di espressione o una stringa contenente un'espressione Python. La stringa o il nodo forniti possono essere costituiti solo dalle seguenti strutture letterali Python: stringhe, numeri, tuple, elenchi, dadi, valori booleani e Nessuno.


6
Per commento qui sotto, questo è pericoloso in quanto esegue semplicemente qualunque pitone sia nella stringa. Quindi, se qualcuno fa una chiamata per cancellare tutto lì, lo farà felicemente.
Paul Kenjora,

16
@PaulKenjora: Stai pensando eval, no ast.literal_eval.
user2357112 supporta Monica

19
ast.literal_evalè più sicuro di eval, ma in realtà non è sicuro . Come spiegano le versioni recenti dei documenti : "Avviso È possibile arrestare in modo anomalo l'interprete Python con una stringa sufficientemente grande / complessa a causa delle limitazioni di profondità dello stack nel compilatore AST di Python." In effetti, può essere possibile eseguire codice arbitrario tramite un attento attacco di impilamento dello stack, sebbene per quanto ne sappia nessuno ha costruito una dimostrazione pubblica del concetto per questo.
abarnert

Bene, ma cosa fare se l'elenco non ha virgolette? ad es. [4 di B, 1 di G]
sqp_125 il

84

Il jsonmodulo è una soluzione migliore ogni volta che esiste un elenco ristretto di dizionari. La json.loads(your_data)funzione può essere utilizzata per convertirla in un elenco.

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

allo stesso modo

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

tuttavia non desidero l'elenco restituito in formato Unicode. ma sembra che anche se ti rimuovo dalla stringa, i dati vengono comunque trattati come Unicode.
Mansoor Akram,

7
Questo funziona per ints ma non per le stringhe nel mio caso perché ogni stringa è a virgolette singole non a virgolette doppie, sospiro.
Paul Kenjora,

4
Secondo il commento di @ PaulKenjora, funziona per '["a","b"]'ma non per "['a','b']".
Skippy le Grand Gourou,

83

Il evalè pericoloso - non è necessario eseguire l'input dell'utente.

Se hai 2.6 o più recenti, usa ast invece di eval:

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

Una volta che hai quello, striple stringhe.

Se utilizzi una versione precedente di Python, puoi avvicinarti molto a ciò che desideri con una semplice espressione regolare:

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

Questo non è buono come la soluzione ast, ad esempio non gestisce correttamente le virgolette di escape nelle stringhe. Ma è semplice, non comporta una valutazione pericolosa e potrebbe essere abbastanza buono per il tuo scopo se sei su un vecchio Python senza ast.


Potresti dirmi per quale motivo hai detto " evalÈ pericoloso - non dovresti eseguire l'input dell'utente."? Sto usando 3.6
Aaryan Dewan il

1
@AaryanDewan se lo usi evaldirettamente, valuterà qualsiasi espressione di pitone valida, che è potenzialmente pericolosa. literal_evalrisolve questo problema valutando solo le strutture letterali di Python: stringhe, numeri, tuple, elenchi, dadi, valori booleani e Nessuno.
Abhishek Menon,

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

10

C'è una soluzione rapida:

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

Gli spazi bianchi indesiderati negli elementi dell'elenco possono essere rimossi in questo modo:

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

ciò preserverebbe ancora gli spazi all'interno delle virgolette
tosh

17
Questo è un invito aperto all'esecuzione di codice arbitrario, MAI fare questo o qualcosa del genere a meno che tu non sappia con assoluta certezza che l'input sarà sempre attendibile al 100%.
Nicholas Knight,

1
Potrei usare questo suggerimento perché sapevo che i miei dati sarebbero sempre stati in quel formato ed era un lavoro di elaborazione dei dati.
Manish Ranjan,

9

Ispirato da alcune delle risposte di cui sopra che funzionano con i pacchetti python di base ho confrontato le prestazioni di alcuni (usando Python 3.7.3):

Metodo 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

Metodo 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

Metodo 3: nessuna importazione

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

Sono rimasto deluso nel vedere quello che ho considerato il metodo con la peggiore leggibilità era il metodo con le migliori prestazioni ... ci sono dei compromessi da considerare quando si va con l'opzione più leggibile ... per il tipo di carichi di lavoro che uso python per me di solito leggibilità del valore su un'opzione leggermente più performante, ma come al solito dipende.


9

Se è solo un elenco monodimensionale, questo può essere fatto senza importare nulla:

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

8
Nota cautelativa: ciò potrebbe essere potenzialmente pericoloso se una delle stringhe all'interno dell'elenco ha una virgola in mezzo.
Hassan Kamal,

Questo non funzionerà se la tua lista di stringhe è una lista di liste
crypdick il

@crypdick Un buon punto, ha aggiunto una nota al riguardo :)
ruohola,

6

Supponendo che tutti i tuoi input siano elenchi e che le doppie virgolette nell'input in realtà non contino, questo può essere fatto con una semplice sostituzione regexp. È un po 'perl-y ma funziona come un fascino. Nota anche che l'output è ora un elenco di stringhe unicode, non hai specificato che ne avevi bisogno, ma sembra avere senso dato l'input 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 variabile junkers contiene una regexp compilata (per la velocità) di tutti i personaggi che non vogliamo, usando] come personaggio che ha richiesto qualche inganno. Re.sub sostituisce tutti questi caratteri con nulla e noi dividiamo la stringa risultante nelle virgole.

Nota che ciò rimuove anche gli spazi dalle voci interne u '["oh no"]' ---> [u'ohno ']. Se questo non è quello che volevi, la regexp deve essere un po 'truccata.


4

Se sai che i tuoi elenchi contengono solo stringhe tra virgolette, questo esempio di pyparsing ti fornirà il tuo elenco di stringhe rimosse (anche preservando l'originalità Unicode).

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

Se le tue liste possono avere più tipi di dati, o anche contenere liste all'interno di liste, avrai bisogno di una grammatica più completa - come questa sul wiki di pyparsing, che gestirà tuple, liste, ints, float e stringhe tra virgolette. Funzionerà con le versioni di Python fino alla 2.4.


mi fai sapere come usare "parseString (). asList ()", se ho questo tipo di stringa: '["A", "B", "C", ["D"]]', come te hanno affermato che anche il pyparsing può farlo. ma sembra che non abbia trovato il modo giusto di farlo.
Mansoor Akram,

"Se i tuoi elenchi possono avere più tipi di dati o addirittura contenere elenchi all'interno di elenchi, avrai bisogno di una grammatica più completa" - consulta il link che ho fornito nella mia risposta per un parser che gestirà elenchi nidificati e vari altri tipi di dati.
PaulMcG

Pyparsing non è più ospitato su wikispaces. L' parsePythonValue.pyesempio è ora su GitHub su github.com/pyparsing/pyparsing/blob/master/examples/…
PaulMcG

1

Per completare ulteriormente la risposta di @Ryan usando json, una funzione molto conveniente per convertire unicode è quella pubblicata qui: https://stackoverflow.com/a/13105359/7599285

ex con virgolette doppie o singole:

>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

Vorrei fornire una soluzione di patterning più intuitiva con regex. La seguente funzione accetta come input un elenco di stringhe contenente stringhe arbitrarie.

Spiegazione graduale: rimuovi tutti gli spazi bianchi, il bracketing e i value_separators (purché non facciano parte dei valori che vuoi estrarre, altrimenti rendi il regex più complesso). Quindi dividi la stringa pulita tra virgolette singole o doppie e prendi i valori non vuoti (o valori indicizzati dispari, qualunque sia la preferenza).

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

testample : "['21'," foo "'6', '0'," A "]"


0

e con puro pitone - non importare alcuna libreria

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

0

È possibile che si verifichino tali problemi durante la gestione dei dati di scrap memorizzati come Pandas DataFrame.

Questa soluzione funziona in modo affascinante se l' elenco di valori è presente come testo .

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

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

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

Nessuna libreria esterna richiesta.


-1

Quindi, seguendo tutte le risposte, ho deciso di programmare i metodi più comuni:

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

Quindi alla fine vince regex!


-1

puoi salvare te stesso .strip () fcn semplicemente tagliando il primo e l'ultimo carattere dalla rappresentazione di stringa dell'elenco (vedi la terza riga sotto)

>>> 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'>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.