2009-02-22 15 views

risposta

16

mi piacerebbe analizzare la stringa se la conversione non riesce:

>>> def convert(s): 
    try: 
     return float(s) 
    except ValueError: 
     num, denom = s.split('/') 
     return float(num)/float(denom) 
... 

>>> convert("0.1234") 
0.1234 

>>> convert("1/2") 
0.5 

Generalmente utilizzando eval è una cattiva idea, in quanto si tratta di un rischio per la sicurezza. Soprattutto se la stringa che viene valutata proviene dall'esterno del sistema.

+1

+1. eval è completamente inutile in questo caso, e sono contento che qualcuno (oltre a me, ovviamente) si sia difeso per quello che è giusto. –

+0

Eval non è un rischio per la sicurezza a meno che i tuoi collaboratori non siano sociopatici dannosi. In questo caso non ne hai bisogno, ma non è più un rischio per la sicurezza che il codice open source stesso. –

+0

Molto buono. La stringa proviene davvero dall'esterno, quindi ho bisogno di andare al sicuro (e cercare di non imparare cattive abitudini). Speravo di non aver bisogno di analizzarlo, ma questo non è male e funziona come un fascino. – ketorin

3

L'operatore / la divisione intera. Prova:

>>> eval("1.0*" + "1/2") 
0.5 

Perché eval() è potenzialmente pericoloso, si dovrebbe sempre controllare esattamente quello che sta passando in esso:

>>> import re 
>>> s = "1/2" 
>>> if re.match(r"\d+/\d+$", s): 
...  eval("1.0*" + s) 
... 
0.5 

Tuttavia, se si va alla briga di corrispondenza dell'ingresso contro una regex in primo luogo, si potrebbe anche usare r"(\d+)/(\d+)$" per estrarre il numeratore e il denominatore, fare la divisione da soli, e del tutto evitare di eval():

>>> m = re.match(r"(\d+)/(\d+)$", s) 
>>> if m: 
...  float(m.group(1))/float(m.group(2)) 
... 
0.5 
+0

Grazie, il suggerimento eval è bello ora e avrebbe fatto il lavoro solo grande ... solo che mi sono reso conto ho bisogno di una soluzione sicura come ha sottolineato su. – ketorin

0

Questo perché 1 e 2 sono interpretati da Python come numeri interi e non float. Deve essere 1.0/2.0 o un mix di quello.

4

Utilizzare from __future__ import division per ottenere il comportamento desiderato. Poi, in un pizzico, si può fare qualcosa di simile

from __future__ import division 
strings = ["0.1234", "1/2", "2/3"] 
numbers = map(eval, strings) 

per ottenere un elenco di carri fuori delle corde. Se si vuole fare questo nel modo "giusto", non usare eval(), ma invece scrivere una funzione che accetta una stringa e chiama float() se non contiene alcuna barra, o analizza la stringa e divide il numeratore e il denominatore se c'è un taglio in esso.

Un modo per farlo:

def parse_float_string(x) 
    parts = x.split('/', 1) 
    if len(parts) == 1: 
     return float(x) 
    elif len(parts) == 2: 
     return float(parts[0])/float(parts[1]) 
    else: 
     raise ValueError 

Poi basta map(parse_float_string, strings) ti porterà la vostra lista.

2

Il problema con eval è che, come in python, il quoziente di interi è un numero intero. Quindi, hai diverse scelte.

Il primo è semplicemente fare ritorno divisione intera galleggia:

from __future__ import division 

L'altro è di dividere il numero razionale:

reduce(lambda x, y: x*y, map(int, rat_str.split("/")), 1) 

Dove rat_str è la stringa con un numero razionale.

0

I suggerimenti con from __future__ import division combinati con eval funzioneranno sicuramente.

E 'probabilmente la pena sottolineare che i suggerimenti che non utilizzano eval ma piuttosto analizzare la stringa fanno perché eval è pericoloso: se c'è qualche modo per una stringa arbitraria per avere inviato a eval, allora il vostro sistema è vulnerabile . Quindi è una cattiva abitudine. (Ma se questo è solo veloce e codice sporco, non è probabilmente che grande un affare!)

7

Come altri hanno fatto notare, utilizzando eval è potenzialmente un rischio per la sicurezza, e certamente una cattiva abitudine per entrare. (se non si pensi che sia così rischioso come exec, immaginare eval ing qualcosa di simile: __import__('os').system('rm -rf /'))

Tuttavia, se si dispone di Python 2.6 o fino, è possibile utilizzare ast.literal_eval, per i quali la stringa fornito:

può consistere solo delle seguenti strutture letterali Python : stringhe, numeri , tuple, elenchi, dicts, booleans e None.

Così dovrebbe essere abbastanza sicuro :-)

+0

Non lo sapevo - grazie! –

+0

Come è possibile valutare qualcosa di malevolo come l'esempio senza avere colleghi che sono seriamente squilibrati? Eval non è più un rischio per la sicurezza che l'accesso alla shell è un rischio per la sicurezza. –

+0

Punto giusto .. Se non si ha a che fare con input non fidati, eval non comporta alcun rischio. Penso di essere influenzato da questo post: http://phpxmlrpc.sourceforge.net/#security - I ragazzi di PHP hanno provato due volte a risolvere i loro problemi, prima di rendersi conto che dovevano sbarazzarsi di eval. Quindi ... –

6

Un'altra opzione (anche solo per 2.6 in su) è il modulo fractions.

>>> from fractions import Fraction 
>>> Fraction("0.1234") 
Fraction(617, 5000) 
>>> Fraction("1/2") 
Fraction(1, 2) 
>>> float(Fraction("0.1234")) 
0.1234 
>>> float(Fraction("1/2")) 
0.5 
1

In Python 3, questo dovrebbe funzionare.

>>> x = ["0.1234", "1/2"] 
>>> [eval(i) for i in x] 
[0.1234, 0.5] 
+1

A destra, in Python 3 hanno reso 'da __future__ import division 'il default ... in Python 3, il futuro è ora! – kquinn

1

sympy può aiutarti qui:

import sympy 

half = sympy.Rational('1/2') 
p1234 = sympy.Rational('0.1234') 
print '%f, %f" % (half, p1234) 
Problemi correlati