2009-02-22 9 views

Respuesta

16

me gustaría analizar la cadena si falla la conversión:

>>> 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 usando eval es una mala idea, ya que es un riesgo para la seguridad. Especialmente si la cadena que se evalúa proviene del exterior del sistema.

+1

+1. eval es completamente innecesario en este caso, y estoy contento de que alguien (que no sea yo, por supuesto) se haya opuesto a lo que es correcto. –

+0

Evaluar no es un riesgo de seguridad a menos que sus compañeros de trabajo sean sociópatas malintencionados. No lo necesita en este caso, pero no es más un riesgo de seguridad que el código abierto en sí mismo. –

+0

Muy bien. La cuerda es de verdad de afuera, así que necesito ir a salvo (y tratar de no aprender los malos hábitos). Esperaba no tener que analizarlo, pero esto no es tan malo y funciona como un encanto. – ketorin

3

El operador / hace la división entera. Proveedores:

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

Debido eval() es potencialmente peligrosa, siempre se debe comprobar exactamente lo que está pasando en ella:

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

Sin embargo, si vas a la molestia de hacer coincidir la entrada contra una expresión regular en primer lugar, que también podría utilizar r"(\d+)/(\d+)$" para extraer el numerador y el denominador, hacer la división de ti mismo, y evitar por completo eval():

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

Gracias, la indirecta eval es bueno ahora y haría el trabajo simplemente genial ... excepto que me di cuenta de que necesito una solución segura según ha apuntado fuera. – ketorin

0

Esto se debe a que Python interpreta 1 y 2 como enteros y no como flotantes. Necesita ser 1.0/2.0 o alguna combinación de eso.

4

Use from __future__ import division para obtener el comportamiento que desea. Luego, en caso de necesidad, se puede hacer algo como

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

para obtener una lista de los flotadores de sus cadenas. Si quiere hacer esto de la manera "correcta", no use eval(), sino que escriba una función que acepte una cadena y llame al float() si no contiene una barra, o analiza la cadena y divide el numerador y el denominador si hay una barra en ella.

Una forma de hacerlo:

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 

A continuación, sólo map(parse_float_string, strings) le conseguirá su lista.

2

El problema con eval es que, como en python, el cociente de enteros es un número entero. Entonces, tienes varias opciones.

La primera es simplemente para hacer el retorno división entera flota:

from __future__ import division 

La otra es la de dividir el número racional:

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

Dónde rat_str es la cadena con un número racional.

0

Las sugerencias con from __future__ import division combinadas con eval ciertamente funcionarán.

Probablemente vale la pena señalar que las sugerencias que no utilizan eval sino más bien analizar la cadena, lo hacen porque eval es peligroso: si hay alguna manera de una cadena arbitraria que son enviadas a eval, entonces su sistema es vulnerable . Entonces es un mal hábito. (Pero si esto es sólo rápido y código sucio, es probable que no que gran cosa!)

7

Como otros han señalado, utilizando eval es potencialmente un riesgo para la seguridad, y sin duda un mal hábito para entrar. (si no se piensa que es tan arriesgado como exec, imaginar eval ing algo como: __import__('os').system('rm -rf /'))

Sin embargo, si usted tiene Python 2.6 o arriba, puede utilizar ast.literal_eval, para los que la cadena entregada:

solamente puede consistir en las siguientes estructuras literales Python: cuerdas, números, tuplas, listas, dicts, booleanos, y ninguno.

Por lo tanto, debe ser bastante seguro :-)

+0

No sabía nada de eso, ¡gracias! –

+0

¿Cómo es posible evaluar algo tan malicioso como el ejemplo sin tener compañeros de trabajo que estén seriamente trastornados? Eval no es más un riesgo de seguridad que el acceso al shell es un riesgo de seguridad. –

+0

Punto justo ... Si no está tratando con datos que no son de confianza, entonces eval no es ningún riesgo. Creo que estoy influenciado por esta publicación: http://phpxmlrpc.sourceforge.net/#security - PHP chicos intentaron dos veces para resolver sus problemas, antes de darse cuenta de que tenían que deshacerse de eval. Entonces ... –

6

Otra opción (también sólo para 2.6 en adelante) es el módulo 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

En Python 3, esto debería funcionar.

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

Correcto, en Python 3 hicieron 'de __future__ división de importación' el predeterminado ... en Python 3, ¡el futuro es ahora! – kquinn

1

sympy puede ayudar a salir aquí:

import sympy 

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