2009-02-22 1 views
9

Как я могу преобразовать строки, которые могут обозначать десятичные или рациональные числа, чтобы поплавкиКак преобразовать строки рационального и десятичного чисел в поплавки в python?

>>> ["0.1234", "1/2"] 
['0.1234', '1/2'] 

Я хочу [0.1234, 0,5].

Eval является то, что я не думал, но не повезло:

>>> eval("1/2") 
0 

ответ

16

Я бы разобрать строку, если преобразование не может быть:

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

Вообще с помощью Eval это плохая идея, так как это создает угрозу безопасности. В частности,, если оцениваемая строка поступает извне системы.

+1

+1. В этом случае eval совершенно ненужен, и я рад, что кто-то (кроме меня, конечно) застрял за то, что правильно. –

+0

Eval не является угрозой безопасности, если ваши сотрудники не являются злонамеренными социопатами. В этом случае вам это не нужно, но это больше не риск для безопасности, чем сам исходный код. –

+0

Очень хорошо. Строка действительно снаружи, поэтому мне нужно идти в безопасное место (и стараться не учиться вредным привычкам). Я надеялся, что вам не придется разбираться, но это не так уж плохо и работает как шарм. – ketorin

3

Оператор / делает целочисленное деление. Попробуйте:

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

Поскольку eval() является потенциально опасным, вы всегда должны проверить, что именно вы передаете в него:

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

Однако, если вы идете в проблему согласования входа на регулярное выражение в первое место, вы можете также использовать r"(\d+)/(\d+)$" для извлечения числитель и знаменатель, сделать разделение самостоятельно, и полностью избежать eval():

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

Спасибо, то Eval намек приятно сейчас и будет делать работу просто большой ... кроме того, что я понял, что необходимо безопасное решение, как заостренный вне. – ketorin

0

Это потому, что 1 и 2 интерпретируются Python как целые числа, а не плавают. Это должно быть 1.0/2.0 или некоторое сочетание этого.

4

Используйте from __future__ import division, чтобы получить требуемое поведение. Затем, в крайнем случае, вы можете сделать что-то вроде

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

, чтобы получить список поплавков из ваших строк. Если вы хотите сделать это «правильным» способом, не используйте eval(), но вместо этого напишите функцию, которая принимает строку, и называет ее float(), если она не содержит ни одной косой черты, или анализирует строку и делит числитель и знаменатель, если есть косой чертой.

Один из способов сделать это:

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 

Тогда просто map(parse_float_string, strings) получит вам список.

2

Проблема с eval заключается в том, что, как и в python, частное от целых чисел является целым числом. Итак, у вас есть несколько вариантов.

Первый просто сделать обратный целочисленное деление поплавки:

from __future__ import division 

Другой разделить рациональное число:

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

Где rat_str это строка с рациональным числом.

0

Предложения с from __future__ import division в сочетании с eval, безусловно, будут работать.

Это, вероятно, стоит отметить, что предложения, которые не используют eval а разобрать строку сделать это, потому что eval опасно: если есть какой-то способ для любой строки, чтобы получить послал к eval, то ваша система уязвима , Так что это плохая привычка. (Но если это просто быстрый и грязный код, это, вероятно, не что большое дело!)

7

Как уже отмечалось, использование eval потенциально создает угрозу безопасности, и, конечно, плохая привычка. (если вы не думаете, что это так рискованно, как exec, представьте eval ИНГ что-то вроде: __import__('os').system('rm -rf /'))

Однако, если у вас есть Python 2.6 или выше, вы можете использовать ast.literal_eval, для которого строка при условии:

может состоять только из следующих Литературные структуры Python: строки, номера, кортежи, списки, dicts, Булевы и None.

Таким образом, это должно быть совершенно безопасно :-)

+0

Не знал об этом - спасибо! –

+0

Как можно считать что-то столь же злым, как пример, не имея коллег, которые серьезно нарушены? Eval больше не является угрозой безопасности, чем доступ к оболочке - это риск для безопасности. –

+0

Справедливая точка. Если вы не имеете дело с ненадежным вводом, тогда eval не рискует. Я думаю, что на меня повлиял этот пост: http://phpxmlrpc.sourceforge.net/#security - парни PHP дважды пытались решить свои проблемы, прежде чем осознать, что им нужно было избавиться от eval. Итак ... –

6

Другой вариант (также только для 2,6 и выше) является 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

В Python 3 это должно работать.

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

Правильно, в Python 3 они сделали 'из __future__ import division' по умолчанию ... в Python 3, будущее сейчас! – kquinn

1

sympy может помочь вам здесь:

import sympy 

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