2016-02-02 25 views
7

mio ingresso è:Converti tuple-stringhe a tuple di stringhe

input = ['(var1,)', '(var2,var3)'] 

risultato atteso è:

output = [('var1',), ('var2','var3')] 

iterazione di ingresso e l'utilizzo di eval/literal_eval sulle tuple-stringhe non è possibile:

>>> eval('(var1,)') 
>>> NameError: name 'var1' is not defined 

Come posso convertire un articolo come '(var1,)' a una tupla in cui gli oggetti interni sono trattati come stringhe anziché come variabili?

Esiste un modo più semplice rispetto alla scrittura di un parser o all'utilizzo della regex?

+1

http://stackoverflow.com/questions/1810109/parsing-a-string-which-represents-a-list-of-tuples – Maroun

+2

@ MarounMaroun Che non funziona come ho spiegato. Nella domanda che hai fornito, i float possono essere valutati. Stringhe come "var1" non possono. 'Literal_eval' genererà un' ValueError: malformed string' – runDOSrun

risposta

12

Per ogni occorrenza di una variabile, eval ricerca nella tabella dei simboli il nome della variabile. E 'possibile fornire una mappatura personalizzata che restituirà il nome della chiave per ogni chiave mancante:

class FakeNamespace(dict): 
    def __missing__(self, key): 
     return key 

Esempio:

In [38]: eval('(var1,)', FakeNamespace()) 
Out[38]: ('var1',) 

In [39]: eval('(var2, var3)', FakeNamespace()) 
Out[39]: ('var2', 'var3') 

Nota: globalieval copie attuali al globals dizionario presentata, se non ha __builtins__. Ciò significa che l'espressione avrà accesso a funzioni, eccezioni e costanti incorporate, nonché variabili nel tuo spazio dei nomi. Si può cercare di risolvere questo problema passando FakeNamespace(__builtins__=<None or some other value>) invece di FakeNamespace(), ma non farà eval sicuro al 100% (Python eval: is it still dangerous if I disable builtins and attribute access?)

+1

Approccio cool! Anche se dovremmo fare attenzione a disinfettare l'input se si usa 'eval()'. – Will

+1

Passa fake_globals sia come globali che come locals all'eval per mitigare gli attacchi - non è possibile accedere a '__builtins__' (né a qualsiasi altro nome Python) se entrambi i dizionari sono cambiati:' In [15]: eval ("__ builtins __ ['zip' ] ", FakeGlobals()) Out [15]: zip', ' eval ("__ builtins __ ['zip']", FakeGlobals(), FakeGlobals()) Tipo Gli indici di stringa di errore devono essere numeri interi – jsbueno

+0

(per quanto sopra commento: l'approccio non è assoluto, ma potrebbe mitigare gli attacchi ingenui) – jsbueno

5

Prova questo:

tuples = [tuple(filter(None, t.strip('()').strip().split(','))) for t in input] 

Ad esempio:

In [16]: tuples = [tuple(filter(None, t.strip('()').strip().split(','))) for t in input] 

In [17]: tuples 
Out[17]: [('var1',), ('var2', 'var3')] 

Stiamo scorrendo la nostra lista di stringhe tuple, e per ciascuno di essi, la rimozione dei () s, quindi dividere la nostra stringa in una lista da ,, e quindi riconvertire la nostra lista in una tupla. Usiamo filter() per rimuovere gli elementi vuoti.

4

Mi piace la soluzione di vaultah. Ecco un altro uno con ast.literal_eval e re se eval non è un'opzione:

>>> import re 
>>> from ast import literal_eval 
>>> [literal_eval(re.sub('(?<=\(|,)(\w+)(?=\)|,)', r'"\1"', x)) for x in input] 
[('var1',), ('var2', 'var3')]