¿Cuál es la mejor manera posible de verificar si una cadena se puede representar como un número en Python?
La función que tengo actualmente en este momento es:
def is_number(s):
try:
float(s)
return True
except ValueError:
return False
Lo cual, no solo es feo y lento, parece torpe. Sin embargo, no he encontrado un método mejor porque llamar float
a la función principal es aún peor.
python
casting
floating-point
type-conversion
Daniel Goldberg
fuente
fuente
x = float('0.00'); if x: use_float(x);
que ahora tiene un error en su código. Los valores de verdad son la razón por la cual estas funciones generan una excepción en lugar de regresarNone
en primer lugar. Una mejor solución es simplemente evitar la función de utilidad y rodear la llamada para flotar en untry catch
momento en que desee usarla.Respuestas:
Yo disputaría ambos.
Un regex u otro método de análisis de cadenas sería más feo y lento.
No estoy seguro de que algo más pueda ser más rápido que lo anterior. Llama a la función y vuelve. Try / Catch no introduce demasiados gastos generales porque la excepción más común se detecta sin una búsqueda exhaustiva de marcos de pila.
El problema es que cualquier función de conversión numérica tiene dos tipos de resultados.
C (como ejemplo) piratea esto de varias maneras. Python lo presenta clara y explícitamente.
Creo que tu código para hacer esto es perfecto.
fuente
try
cláusula, por lo que pondría elreturn True
de unaelse
cláusula de latry
. Una de las razones es que con el código en la pregunta, si tuviera que revisarlo, tendría que verificar que la segunda declaración en latry
cláusula no pueda generar un ValueError: concedido, esto no requiere demasiado tiempo o poder mental, pero ¿por qué usar alguno cuando no se necesita ninguno?IsNumeric()
, termino con un try / catch u otro que envuelve un try / catch. Ughif is_number(s): x = float(x) else: // fail
es la misma cantidad de líneas de código quetry: x = float(x) catch TypeError: # fail
. Esta función de utilidad es una abstracción completamente innecesaria.En caso de que esté buscando enteros de análisis (positivo, sin signo) en lugar de flotantes, puede usar la
isdigit()
función para objetos de cadena.Métodos de cadena
isdigit()
: Python2 , Python3También hay algo en las cadenas Unicode, que no estoy muy familiarizado con Unicode: es decimal / decimal
fuente
isdigit()
yint()
tiene diferentes opiniones sobre lo que es un número entero, por ejemplo, para el carácter Unicodeu'\u00b9'
:u'¹'.isdigit()
esTrue
peroint(u'¹')
aumenta ValueError.TL; DR La mejor solución es
s.replace('.','',1).isdigit()
Hice algunos puntos de referencia comparando los diferentes enfoques
Si la cadena no es un número, el bloque except es bastante lento. Pero lo más importante, el método try-except es el único enfoque que maneja las anotaciones científicas correctamente.
La notación flotante ".1234" no es compatible con:
- is_number_regex
La notación científica "1.000000e + 50" no es compatible con:
- is_number_regex
- is_number_repl_isdigit
La notación científica "1e50" no es compatible con:
- is_number_regex
- is_number_repl_isdigit
EDITAR: los resultados de referencia
donde se probaron las siguientes funciones
fuente
s.replace('.','',1).isdigit()
) debería aparecer al comienzo de esta respuesta. En cualquier caso, debe ser el aceptado. ¡Gracias!'1.5e-9'
o en negativos.Hay una excepción que es posible que desee tener en cuenta: la cadena 'NaN'
Si desea que is_number devuelva FALSE para 'NaN', este código no funcionará, ya que Python lo convierte en su representación de un número que no es un número (hablemos de problemas de identidad):
De lo contrario, debería agradecerle el código que ahora uso ampliamente. :)
SOL.
fuente
NaN
podría ser un buen valor para devolver (en lugar deFalse
) si el texto pasado no es en realidad una representación de un número. Verificarlo es una molestia (elfloat
tipo de Python realmente necesita un método para ello) pero puede usarlo en los cálculos sin producir un error, y solo necesita verificar el resultado.'inf'
. Oinf
oNaN
también se puede prefijar con un+
o-
y aún ser aceptado.x-1 == x
es cierto para flotadores grandes más pequeños queinf
. Desde Python 3.2 puede usarmath.isfinite
para probar números que no son NaN ni infinitos, o verificar ambosmath.isnan
ymath.isinf
antes de eso.Qué tal esto:
que devolverá verdadero solo si hay uno o no '.' en la cadena de dígitos.
devolverá falso
editar: acabo de ver otro comentario ... se puede agregar un
.replace(badstuff,'',maxnum_badstuff)
para otros casos. si está pasando sal y no condimentos arbitrarios (ref: xkcd # 974 ) esto funcionará bien: Pfuente
1.234e56
(que también podrían escribirse como+1.234E+56
y varias variantes más).re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')
debería hacer un mejor trabajo para determinar un número (pero no todos, no estoy reclamando eso). No recomiendo usar esto, mucho mejor usar el código original del interlocutor.Puede tomar un tiempo acostumbrarse, pero esta es la forma pitónica de hacerlo. Como ya se señaló, las alternativas son peores. Pero hay otra ventaja de hacer las cosas de esta manera: el polimorfismo.
La idea central detrás del tipeo de patos es que "si camina y habla como un pato, entonces es un pato". ¿Qué sucede si decide que necesita subclasificar una cadena para poder cambiar la forma en que determina si algo se puede convertir en un flotador? ¿O qué pasa si decides probar algún otro objeto por completo? Puede hacer estas cosas sin tener que cambiar el código anterior.
Otros idiomas resuelven estos problemas mediante el uso de interfaces. Guardaré el análisis de qué solución es mejor para otro hilo. El punto, sin embargo, es que Python está decididamente del lado de la ecuación, y probablemente tendrá que acostumbrarse a una sintaxis como esta si planea hacer mucha programación en Python (pero eso no significa te tiene que gustar, por supuesto).
Otra cosa que quizás desee tener en cuenta: Python es bastante rápido en lanzar y capturar excepciones en comparación con muchos otros lenguajes (30 veces más rápido que .Net, por ejemplo). Diablos, el lenguaje en sí mismo incluso arroja excepciones para comunicar condiciones de programa normales y no excepcionales (cada vez que usa un bucle for). Por lo tanto, no me preocuparía demasiado por los aspectos de rendimiento de este código hasta que note un problema importante.
fuente
hasattr()
que solo hay unagetattr()
llamada envuelta en untry/except
. Aún así, el manejo de excepciones es más lento que el control de flujo normal, por lo que usarlo para algo que será cierto la mayoría de las veces puede resultar en una penalización de rendimiento.Actualizado después de que Alfe señaló que no es necesario verificar la flotación por separado, ya que el complejo maneja ambos:
Dicho anteriormente: es posible que también necesite verificar números complejos (por ejemplo, 1 + 2i), que no pueden representarse mediante un flotante:
fuente
float()
cosas por completo y simplemente verificar si lacomplex()
llamada tiene éxito. Todo lo analizadofloat()
puede ser analizado porcomplex()
.complex('(01989)')
devolverá(1989+0j)
. Perofloat('(01989)')
fallará. Así que creo que usarcomplex
no es una buena idea.Para
int
usar esto:Pero para
float
eso necesitamos algunos trucos ;-). Cada número flotante tiene un punto ...También para números negativos solo agregue
lstrip()
:Y ahora obtenemos una forma universal:
fuente
1.234e56
y similares. Además, me interesaría cómo descubrirías que99999999999999999999e99999999999999999999
no es un número. Intentando analizarlo se entera rápidamente.Solo Mimic C #
En C # hay dos funciones diferentes que manejan el análisis de valores escalares:
float.parse ():
Nota: Si se pregunta por qué cambié la excepción a un TypeError, aquí está la documentación .
float.try_parse ():
Nota: No desea devolver el booleano 'False' porque todavía es un tipo de valor. Ninguno es mejor porque indica falla. Por supuesto, si desea algo diferente, puede cambiar el parámetro de falla a lo que desee.
Para extender float para incluir 'parse ()' y 'try_parse ()', necesitará hacer un parche en la clase 'float' para agregar estos métodos.
Si desea respetar las funciones preexistentes, el código debería ser algo como:
Nota al margen: Personalmente prefiero llamarlo Monkey Punching porque siento que estoy abusando del lenguaje cuando hago esto, pero YMMV.
Uso:
Y el gran sabio Pythonas le dijo a la Santa Sede Sharpisus: "Todo lo que puedas hacer lo puedo hacer mejor; puedo hacer cualquier cosa mejor que tú".
fuente
!
lugar denot
podría ser un error menor, pero definitivamente no puede asignar atributos alfloat
CPython incorporado .Para cadenas de no números, en
try: except:
realidad es más lento que las expresiones regulares. Para cadenas de números válidos, la expresión regular es más lenta. Entonces, el método apropiado depende de su entrada.Si encuentra que está en un enlace de rendimiento, puede usar un nuevo módulo de terceros llamado fastnumbers que proporciona una función llamada isfloat . Divulgación completa, soy el autor. He incluido sus resultados en los horarios a continuación.
Como puedes ver
try: except:
fue rápido para la entrada numérica pero muy lento para una entrada no válidafastnumbers
gana en ambos casosfuente
prep_code_basis
yprep_code_re_method
hubiera evitado mi error.isfloat
función?str(s).strip('-').replace('.','',1).isdigit()
es aproximadamente 10 veces más lento!Sé que esto es particularmente antiguo, pero agregaría una respuesta que creo que cubre la información que falta en la respuesta más votada que podría ser muy valiosa para cualquiera que encuentre esto:
Para cada uno de los siguientes métodos, conéctelos con un recuento si necesita que se acepte alguna entrada. (Suponiendo que estamos usando definiciones vocales de enteros en lugar de 0-255, etc.)
x.isdigit()
funciona bien para verificar si x es un número entero.x.replace('-','').isdigit()
funciona bien para verificar si x es negativo. (Verificar - en la primera posición)x.replace('.','').isdigit()
funciona bien para verificar si x es un decimal.x.replace(':','').isdigit()
funciona bien para verificar si x es una relación.x.replace('/','',1).isdigit()
funciona bien para verificar si x es una fracción.fuente
x.replace('/','',1).isdigit()
o fechas como el 4/7/2017 se interpretarían erróneamente como números.Esta respuesta proporciona una guía paso a paso que tiene función con ejemplos para encontrar la cadena es:
Compruebe si la cadena es un entero positivo
Puede usar
str.isdigit()
para verificar si la cadena dada es un entero positivo .Resultados de muestra:
Verifique que la cadena sea positiva / negativa - entero / flotante
str.isdigit()
devuelveFalse
si la cadena es un número negativo o un número flotante. Por ejemplo:Si desea verificar también los enteros negativos y
float
, entonces puede escribir una función personalizada para verificarlo como:Ejecución de muestra:
Descarte las cadenas "NaN" (no un número) mientras verifica el número
Las funciones anteriores volverán
True
para la cadena "NAN" (No es un número) porque para Python es un valor flotante válido que lo representa no es un número. Por ejemplo:Para verificar si el número es "NaN", puede usarlo
math.isnan()
como:O si no desea importar una biblioteca adicional para verificar esto, simplemente puede verificarlo comparándolo con él mismo
==
. Python regresaFalse
cuandonan
se compara el flotador consigo mismo. Por ejemplo:Por lo tanto, por encima de la función
is_number
se puede actualizar para volverFalse
a"NaN"
como:Ejecución de muestra:
PD: cada operación para cada verificación, dependiendo del tipo de número, viene con una sobrecarga adicional. Elija la versión de la
is_number
función que se ajuste a sus necesidades.fuente
Lanzar para flotar y capturar ValueError es probablemente la forma más rápida, ya que float () está específicamente diseñado para eso. Cualquier otra cosa que requiera el análisis de cadenas (expresiones regulares, etc.) probablemente será más lenta debido al hecho de que no está ajustada para esta operación. Mis $ 0.02.
fuente
Puede usar cadenas Unicode, tienen un método para hacer exactamente lo que desea:
O:
http://www.tutorialspoint.com/python/string_isnumeric.htm
http://docs.python.org/2/howto/unicode.html
fuente
s.isdecimal()
comprueba si las
cadena es un entero no negativo.s.isnumeric()
incluye personajes queint()
rechaza.Quería ver qué método es el más rápido. En general, los resultados mejores y más consistentes fueron dados por la
check_replace
función. Lacheck_exception
función dio los resultados más rápidos , pero solo si no se activó ninguna excepción, lo que significa que su código es el más eficiente, pero la sobrecarga de lanzar una excepción es bastante grande.Tenga en cuenta que la comprobación de una conversión exitosa es el único método que es exacto, por ejemplo, esto funciona
check_exception
pero las otras dos funciones de prueba devolverán False para un flotante válido:Aquí está el código de referencia:
Estos son los resultados con Python 2.7.10 en una MacBook Pro 13 2017:
Aquí están los resultados con Python 3.6.5 en una MacBook Pro 13 2017:
Estos son los resultados con PyPy 2.7.13 en una MacBook Pro 13 2017:
fuente
Entonces, para poner todo junto, verificar Nan, infinito y números complejos (parece que están especificados con j, no i, es decir, 1 + 2j) da como resultado:
fuente
La entrada puede ser la siguiente:
a="50"
b=50
c=50.1
d="50.1"
1-Entrada general:
¡La entrada de esta función puede ser todo!
Encuentra si la variable dada es numérica. Las cadenas numéricas consisten en signos opcionales, cualquier número de dígitos, parte decimal opcional y parte exponencial opcional. Por lo tanto, + 0123.45e6 es un valor numérico válido. La notación hexadecimal (por ejemplo, 0xf4c3b00c) y binaria (por ejemplo, 0b10100111001) no está permitida.
is_numeric function
prueba:
función is_float
Encuentra si la variable dada es flotante. las cadenas flotantes consisten en signos opcionales, cualquier número de dígitos, ...
prueba:
lo que es ast ?
2- Si estás seguro de que el contenido variable es String :
use el método str.isdigit ()
Entrada 3-numérica:
detectar el valor int:
detectar flotador:
fuente
ast
"?Hice alguna prueba de velocidad. Digamos que si es probable que la cadena sea un número, la estrategia try / except es la más rápida posible. Si no es probable que la cadena sea un número y usted esté interesado en la verificación de enteros , vale la pena hacer alguna prueba (es decir, el título más el título '-'). Si está interesado en verificar el número flotante, debe usar el código try / except sin escape.
fuente
Necesitaba determinar si una cadena se convertía en tipos básicos (float, int, str, bool). Después de no encontrar nada en Internet, creé esto:
Ejemplo
Puedes capturar el tipo y usarlo
fuente
RyanN sugiere
Pero esto no funciona del todo, porque para flotadores suficientemente grandes,
x-1 == x
devuelve verdadero. Por ejemplo,2.0**54 - 1 == 2.0**54
fuente
Creo que la solución está muy bien, pero no es una implementación correcta de expresiones regulares.
Parece haber mucho odio regexp hacia estas respuestas, lo que creo que no está justificado, las expresiones regulares pueden ser razonablemente limpias, correctas y rápidas. Realmente depende de lo que intentes hacer. La pregunta original era cómo puede "verificar si una cadena se puede representar como un número (flotante)" (según su título). Presumiblemente, querrá usar el valor numérico / flotante una vez que haya verificado que es válido, en cuyo caso su intento / excepción tiene mucho sentido. Pero si, por alguna razón, solo desea validar que una cadena es un númeroentonces una expresión regular también funciona bien, pero es difícil de corregir. Creo que la mayoría de las respuestas de expresiones regulares hasta ahora, por ejemplo, no analizan correctamente las cadenas sin una parte entera (como ".7") que es flotante en lo que respecta a Python. Y eso es un poco difícil de verificar en una sola expresión regular donde no se requiere la porción fraccional. He incluido dos expresiones regulares para mostrar esto.
Plantea la interesante pregunta de qué es un "número". ¿Incluye "inf" que es válido como flotante en Python? ¿O incluye números que son "números" pero que tal vez no se puedan representar en Python (como los números que son más grandes que el máximo flotante).
También hay ambigüedades en cómo analiza los números. Por ejemplo, ¿qué pasa con "--20"? ¿Es este un "número"? ¿Es esta una forma legal de representar "20"? Python le permitirá hacer "var = --20" y establecerlo en 20 (aunque en realidad esto se debe a que lo trata como una expresión), pero float ("- 20") no funciona.
De todos modos, sin más información, aquí hay una expresión regular que creo que cubre todas las entradas y flotadores a medida que Python las analiza .
Algunos valores de prueba de ejemplo:
La ejecución del código de evaluación comparativa en la respuesta de @ ron-reiter muestra que esta expresión regular es en realidad más rápida que la expresión regular normal y es mucho más rápida en el manejo de valores incorrectos que la excepción, lo que tiene sentido. Resultados:
fuente
fuente
1e6
representar un número?Aquí está mi manera simple de hacerlo. Digamos que estoy recorriendo algunas cadenas y quiero agregarlas a una matriz si resultan ser números.
Reemplace myvar.apppend con cualquier operación que desee hacer con la cadena si resulta ser un número. La idea es intentar usar una operación float () y usar el error devuelto para determinar si la cadena es o no un número.
fuente
También utilicé la función que mencionaste, pero pronto noté que las cadenas como "Nan", "Inf" y su variación se consideran números. Por lo tanto, le propongo una versión mejorada de su función, que devolverá falso en ese tipo de entrada y no fallará las variantes "1e3":
fuente
Este código maneja los exponentes, flotantes y enteros, sin usar expresiones regulares.
fuente
Función de ayuda del usuario:
entonces
fuente
Puede generalizar la técnica de excepción de una manera útil devolviendo valores más útiles que Verdadero y Falso. Por ejemplo, esta función pone comillas alrededor de las cadenas pero deja solo los números. Que es justo lo que necesitaba para un filtro rápido y sucio para hacer algunas definiciones variables para R.
fuente
Estaba trabajando en un problema que me llevó a este hilo, a saber, cómo convertir una colección de datos en cadenas y números de la manera más intuitiva. Después de leer el código original, me di cuenta de que lo que necesitaba era diferente de dos maneras:
1 - Quería un resultado entero si la cadena representaba un entero
2 - Quería que un número o un resultado de cadena se pegara a una estructura de datos
así que adapté el código original para producir esta derivada:
fuente
Prueba esto.
fuente
is_number('10')
fuente