2009-02-22 9 views

Odpowiedz

16

będę analizować ciąg jeśli konwersja nie powiedzie:

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

Ogólnie używając eval jest zły pomysł, ponieważ jest to zagrożenie bezpieczeństwa. W szczególności, jeśli oceniany łańcuch pochodzi spoza systemu.

+1

+1. eval jest zupełnie niepotrzebny w tym przypadku i cieszę się, że ktoś (oprócz mnie, oczywiście) utknął w słuszności tego, co słuszne. –

+0

Eval nie stanowi zagrożenia dla bezpieczeństwa, chyba że Twoi współpracownicy są złośliwymi socjopatami. W tym przypadku nie jest to potrzebne, ale nie stanowi większego zagrożenia dla bezpieczeństwa niż sam kod open source. –

+0

Bardzo dobrze. Sznurek jest rzeczywiście z zewnątrz, więc muszę iść bezpiecznie (i staram się nie uczyć złych nawyków). Miałem nadzieję, że nie będę musiał tego analizować, ale nie jest tak źle i działa jak czar. – ketorin

3

Operator / robi podział całkowitej. Spróbuj:

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

Ponieważ eval() jest potencjalnie niebezpieczna, należy zawsze sprawdzić dokładnie co przechodzą do niego:

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

Jednakże, jeśli sobie trud dopasowania wejście przeciwko regex w pierwsze miejsce, równie dobrze można użyć r"(\d+)/(\d+)$" wyodrębnić licznika i mianownika, czy podział siebie i całkowicie uniknąć eval():

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

Dzięki, wskazówka eval jest miło i teraz by wykonać zadanie po prostu wielki ... oprócz tego, że zdałem sobie sprawę, muszę bezpieczne rozwiązanie jak wskazano na zewnątrz. – ketorin

0

To dlatego, że 1 i 2 są interpretowane przez Pythona jako liczby całkowite, a nie zmiennoprzecinkowe. Musi to być 1.0/2.0 lub jakaś mieszanka tego.

4

Użyj from __future__ import division, aby uzyskać pożądane zachowanie. Następnie, możesz zrobić coś takiego, jak

from __future__ import division 
strings = ["0.1234", "1/2", "2/3"] 
numbers = map(eval, strings) 
, aby uzyskać listę elementów pływających z łańcuchów. Jeśli chcesz zrobić to w odpowiedni sposób, nie używaj eval(), ale zamiast tego napisz funkcję, która akceptuje ciąg znaków i wywołuje na nim float(), jeśli nie zawiera ukośnika, lub analizuje ciąg znaków i dzieli licznik i mianownik, jeśli istnieje cięcie w tym.

Jednym ze sposobów, aby to zrobić:

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 

Wtedy właśnie map(parse_float_string, strings) będzie Ci listę.

2

Problem z eval polega na tym, że podobnie jak w pythonie, iloraz liczb całkowitych jest liczbą całkowitą. Masz kilka możliwości.

Pierwszym z nich jest po prostu dokonać powrót podział całkowitą pływa:

from __future__ import division 

Drugi polega na podzieleniu liczby Rational:

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

Gdzie rat_str jest łańcuch z racjonalnej liczby.

0

Propozycje z w połączeniu z eval z pewnością zadziałają.

To chyba warto zauważyć, że propozycje, które nie korzystają eval ale raczej analizować ciąg zrobić, ponieważ eval jest niebezpieczna, czy istnieje jakiś sposób na dowolny ciąg wysyłane do eval, to system jest podatny . To zły nawyk. (Ale jeśli to tylko szybkie i zabrudzony kodu, to prawdopodobnie nie że wielka sprawa!)

7

Jak inni zwrócili uwagę, używając eval jest potencjalnie zagrożenie dla bezpieczeństwa, a na pewno zły nawyk, aby dostać się. (jeśli uważasz, że nie jest tak ryzykowne, jak exec, wyobraź eval ing coś takiego: __import__('os').system('rm -rf /'))

Jednak jeśli masz Pythona 2.6 lub górę, można użyć ast.literal_eval, dla których ciąg warunkiem:

może składać się tylko z następujących literałowych struktur literowych: ciągi znaków, liczb, krotek, list, dykt, znaków logicznych i brak.

Zatem powinno być dość bezpieczne :-)

+0

Nie wiedziałem o tym - dzięki! –

+0

Jak to możliwe, aby ocenić coś tak złośliwego, jak przykład, bez współpracowników, którzy są poważnie obłąkani? Eval nie stanowi większego zagrożenia dla bezpieczeństwa niż dostęp do powłoki jest zagrożeniem bezpieczeństwa. –

+0

Fajny punkt. Jeśli nie masz do czynienia z niezaufanym wejściem, eval nie stanowi ryzyka. Myślę, że jestem pod wpływem tego postu: http://phpxmlrpc.sourceforge.net/#security - faceci PHP dwukrotnie próbowali rozwiązać swoje problemy, zanim zdali sobie sprawę, że musieli pozbyć się ewaluacji. Więc ... –

6

Inną opcją (również tylko dla 2,6 i więcej) to moduł 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

W Pythonie 3 powinno to działać.

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

Dobrze, w Pythonie 3 zrobiono 'z __future__ import division' domyślnie ... w Pythonie 3, przyszłość jest teraz! – kquinn

1

sympy może pomóc tutaj:

import sympy 

half = sympy.Rational('1/2') 
p1234 = sympy.Rational('0.1234') 
print '%f, %f" % (half, p1234) 
Powiązane problemy