¿Hay alguna manera de saber si una cadena representa un número entero (por ejemplo '3'
, '-17'
pero no '3.14'
o 'asfasfas'
) sin usar un mecanismo try / except?
is_int('3.14') = False
is_int('-7') = True
¿Hay alguna manera de saber si una cadena representa un número entero (por ejemplo '3'
, '-17'
pero no '3.14'
o 'asfasfas'
) sin usar un mecanismo try / except?
is_int('3.14') = False
is_int('-7') = True
Respuestas:
Si realmente está molesto por usar try/except
s en todo el lugar, simplemente escriba una función auxiliar:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Va a ser MUCHO más código para cubrir exactamente todas las cadenas que Python considera enteros. Yo digo que sea pitón en este caso.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
con enteros positivos que podrías usar .isdigit
:
>>> '16'.isdigit()
True
Sin embargo, no funciona con enteros negativos. supongamos que puede intentar lo siguiente:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
no funcionará con el '16.0'
formato, que es similar al int
casting en este sentido.
editar :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
es cierto pero int(u'²')
aumenta ValueError. Usar en su u.isdecimal()
lugar. str.isdigit()
depende de la configuración regional en Python 2.
check_int('')
False
Sabes, he descubierto (y he probado esto una y otra vez) que intentar / excepto no funciona tan bien, por cualquier razón. Frecuentemente intento varias formas de hacer las cosas, y no creo haber encontrado un método que use try / except para obtener el mejor rendimiento de los probados, de hecho, me parece que esos métodos generalmente se han acercado al lo peor, si no lo peor. No en todos los casos, pero en muchos casos. Sé que mucha gente dice que es la forma "Pitónica", pero esa es un área donde me separo de ellos. Para mí, no es muy eficiente ni muy elegante, por lo tanto, tiendo a usarlo solo para capturar errores e informar.
Iba a quejarme de que PHP, perl, ruby, C e incluso el maldito shell tienen funciones simples para probar una cadena de entero, ¡pero la diligencia debida en verificar esas suposiciones me hizo tropezar! Al parecer, esta falta es una enfermedad común.
Aquí hay una edición rápida y sucia de la publicación de Bruno:
import sys, time, re
g_intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
testvals = [
# integers
0, 1, -1, 1.0, -1.0,
'0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '06',
# non-integers
'abc 123',
1.1, -1.1, '1.1', '-1.1', '+1.1',
'1.1.1', '1.1.0', '1.0.1', '1.0.0',
'1.0.', '1..0', '1..',
'0.0.', '0..0', '0..',
'one', object(), (1,2,3), [1,2,3], {'one':'two'},
# with spaces
' 0 ', ' 0.', ' .0','.01 '
]
def isInt_try(v):
try: i = int(v)
except: return False
return True
def isInt_str(v):
v = str(v).strip()
return v=='0' or (v if v.find('..') > -1 else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()
def isInt_re(v):
import re
if not hasattr(isInt_re, 'intRegex'):
isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
return isInt_re.intRegex.match(str(v).strip()) is not None
def isInt_re2(v):
return g_intRegex.match(str(v).strip()) is not None
def check_int(s):
s = str(s)
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
def timeFunc(func, times):
t1 = time.time()
for n in range(times):
for v in testvals:
r = func(v)
t2 = time.time()
return t2 - t1
def testFuncs(funcs):
for func in funcs:
sys.stdout.write( "\t%s\t|" % func.__name__)
print()
for v in testvals:
if type(v) == type(''):
sys.stdout.write("'%s'" % v)
else:
sys.stdout.write("%s" % str(v))
for func in funcs:
sys.stdout.write( "\t\t%s\t|" % func(v))
sys.stdout.write("\r\n")
if __name__ == '__main__':
print()
print("tests..")
testFuncs((isInt_try, isInt_str, isInt_re, isInt_re2, check_int))
print()
print("timings..")
print("isInt_try: %6.4f" % timeFunc(isInt_try, 10000))
print("isInt_str: %6.4f" % timeFunc(isInt_str, 10000))
print("isInt_re: %6.4f" % timeFunc(isInt_re, 10000))
print("isInt_re2: %6.4f" % timeFunc(isInt_re2, 10000))
print("check_int: %6.4f" % timeFunc(check_int, 10000))
Aquí están los resultados de la comparación de rendimiento:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
El método de CA podría escanearlo una vez, y listo. Creo que el método AC que escanea la cadena una vez sería lo correcto.
EDITAR:
He actualizado el código anterior para que funcione en Python 3.5, para incluir la función check_int de la respuesta más votada actualmente, y para usar la expresión regular más popular actual que puedo encontrar para probar el integer-hood. Esta expresión regular rechaza cadenas como 'abc 123'. He agregado 'abc 123' como valor de prueba.
Es muy interesante para mí notar, en este punto, que NINGUNA de las funciones probadas, incluido el método try, la popular función check_int y la expresión regular más popular para probar el entero, devuelve las respuestas correctas para todas las valores de la prueba (bueno, dependiendo de cuáles creas que son las respuestas correctas; mira los resultados de la prueba a continuación).
La función integrada int () trunca silenciosamente la parte fraccionaria de un número de coma flotante y devuelve la parte entera antes del decimal, a menos que el número de coma flotante se convierta primero en una cadena.
La función check_int () devuelve falso para valores como 0.0 y 1.0 (que técnicamente son enteros) y devuelve verdadero para valores como '06'.
Aquí están los resultados de la prueba actual (Python 3.5):
isInt_try | isInt_str | isInt_re | isInt_re2 | check_int |
0 True | True | True | True | True |
1 True | True | True | True | True |
-1 True | True | True | True | True |
1.0 True | True | False | False | False |
-1.0 True | True | False | False | False |
'0' True | True | True | True | True |
'0.' False | True | False | False | False |
'0.0' False | True | False | False | False |
'1' True | True | True | True | True |
'-1' True | True | True | True | True |
'+1' True | True | True | True | True |
'1.0' False | True | False | False | False |
'-1.0' False | True | False | False | False |
'+1.0' False | True | False | False | False |
'06' True | True | False | False | True |
'abc 123' False | False | False | False | False |
1.1 True | False | False | False | False |
-1.1 True | False | False | False | False |
'1.1' False | False | False | False | False |
'-1.1' False | False | False | False | False |
'+1.1' False | False | False | False | False |
'1.1.1' False | False | False | False | False |
'1.1.0' False | False | False | False | False |
'1.0.1' False | False | False | False | False |
'1.0.0' False | False | False | False | False |
'1.0.' False | False | False | False | False |
'1..0' False | False | False | False | False |
'1..' False | False | False | False | False |
'0.0.' False | False | False | False | False |
'0..0' False | False | False | False | False |
'0..' False | False | False | False | False |
'one' False | False | False | False | False |
<obj..> False | False | False | False | False |
(1, 2, 3) False | False | False | False | False |
[1, 2, 3] False | False | False | False | False |
{'one': 'two'} False | False | False | False | False |
' 0 ' True | True | True | True | False |
' 0.' False | True | False | False | False |
' .0' False | False | False | False | False |
'.01 ' False | False | False | False | False |
Justo ahora intenté agregar esta función:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Funciona casi tan bien como check_int (0.3486) y devuelve verdadero para valores como 1.0 y 0.0 y +1.0 y 0. y .0 y así sucesivamente. Pero también devuelve verdadero para '06', entonces. Elige tu veneno, supongo.
try
es más eficiente: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
debería hacer el truco.
Ejemplos:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDITAR : Como señaló @BuzzMoschetti, esta forma fallará para el número menos (por ejemplo, "-23" ). En caso de que su input_num pueda ser menor que 0, use re.sub (regex_search, regex_replace, contents) antes de aplicar str.isdigit () . Por ejemplo:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Usa una expresión regular:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Si debe aceptar fracciones decimales también:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Para un mejor rendimiento, si está haciendo esto a menudo, compilar la expresión regular el uso de una sola vez re.compile()
.
La solución RegEx adecuada combinaría las ideas de Greg Hewgill y Nowell, pero no utilizaría una variable global. Puede lograr esto adjuntando un atributo al método. Además, sé que está mal visto poner las importaciones en un método, pero lo que busco es un efecto de "módulo perezoso" como http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
editar: mi técnica favorita hasta ahora es utilizar exclusivamente métodos del objeto String.
#!/usr/bin/env python
# Uses exclusively methods of the String object
def isInteger(i):
i = str(i)
return i=='0' or (i if i.find('..') > -1 else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()
# Uses re module for regex
def isIntegre(i):
import re
if not hasattr(isIntegre, '_re'):
print("I compile only once. Remove this line when you are confident in that.")
isIntegre._re = re.compile(r"[-+]?\d+(\.0*)?$")
return isIntegre._re.match(str(i)) is not None
# When executed directly run Unit Tests
if __name__ == '__main__':
for obj in [
# integers
0, 1, -1, 1.0, -1.0,
'0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0',
# non-integers
1.1, -1.1, '1.1', '-1.1', '+1.1',
'1.1.1', '1.1.0', '1.0.1', '1.0.0',
'1.0.', '1..0', '1..',
'0.0.', '0..0', '0..',
'one', object(), (1,2,3), [1,2,3], {'one':'two'}
]:
# Notice the integre uses 're' (intended to be humorous)
integer = ('an integer' if isInteger(obj) else 'NOT an integer')
integre = ('an integre' if isIntegre(obj) else 'NOT an integre')
# Make strings look like strings in the output
if isinstance(obj, str):
obj = ("'%s'" % (obj,))
print("%30s is %14s is %14s" % (obj, integer, integre))
Y para los miembros menos aventureros de la clase, aquí está el resultado:
I compile only once. Remove this line when you are confident in that.
0 is an integer is an integre
1 is an integer is an integre
-1 is an integer is an integre
1.0 is an integer is an integre
-1.0 is an integer is an integre
'0' is an integer is an integre
'0.' is an integer is an integre
'0.0' is an integer is an integre
'1' is an integer is an integre
'-1' is an integer is an integre
'+1' is an integer is an integre
'1.0' is an integer is an integre
'-1.0' is an integer is an integre
'+1.0' is an integer is an integre
1.1 is NOT an integer is NOT an integre
-1.1 is NOT an integer is NOT an integre
'1.1' is NOT an integer is NOT an integre
'-1.1' is NOT an integer is NOT an integre
'+1.1' is NOT an integer is NOT an integre
'1.1.1' is NOT an integer is NOT an integre
'1.1.0' is NOT an integer is NOT an integre
'1.0.1' is NOT an integer is NOT an integre
'1.0.0' is NOT an integer is NOT an integre
'1.0.' is NOT an integer is NOT an integre
'1..0' is NOT an integer is NOT an integre
'1..' is NOT an integer is NOT an integre
'0.0.' is NOT an integer is NOT an integre
'0..0' is NOT an integer is NOT an integre
'0..' is NOT an integer is NOT an integre
'one' is NOT an integer is NOT an integre
<object object at 0x103b7d0a0> is NOT an integer is NOT an integre
(1, 2, 3) is NOT an integer is NOT an integre
[1, 2, 3] is NOT an integer is NOT an integre
{'one': 'two'} is NOT an integer is NOT an integre
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False
Entonces su función sería:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
Al enfoque de Greg Hewgill le faltaban algunos componentes: el "^" inicial para coincidir solo con el inicio de la cadena y compilar el re de antemano. Pero este enfoque le permitirá evitar un intento: exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Me interesaría por qué intenta evitar intentar: ¿excepto?
Tengo que hacer esto todo el tiempo, y tengo una aversión leve pero ciertamente irracional a usar el patrón try / except. Yo uso esto:
all([xi in '1234567890' for xi in x])
No admite números negativos, por lo que puede eliminar un signo menos (si lo hay) y luego verificar si el resultado comprende dígitos del 0 al 9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
También puede pasar x a str () si no está seguro de que la entrada es una cadena:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Hay al menos dos casos (¿de borde?) En los que esto se desmorona:
type(1E2)
da <class 'float'>
mientras type(10^2)
da <class 'int'>
.Por lo tanto, no funcionará para cada entrada posible, pero si puede excluir la notación científica, la notación exponencial y las cadenas vacías, es una comprobación de una línea correcta que devuelve False
si x no es un número entero y True
si x es un número entero.
No sé si es pitónico, pero es una línea y está relativamente claro lo que hace el código.
all(xi in '1234567890' for xi in x])
patrón parece más pedir permiso para caminar por el césped. No estoy emocionado de pedir permiso, pero aquí estamos.
Yo creo que
s.startswith('-') and s[1:].isdigit()
sería mejor reescribir a:
s.replace('-', '').isdigit()
porque s [1:] también crea una nueva cadena
Pero la solución mucho mejor es
s.lstrip('+-').isdigit()
replace
hace? Además, esto aceptará incorrectamente 5-2
, por ejemplo.
s='-'
Realmente me gustó la publicación de Shavais, pero agregué un caso de prueba más (y la función isdigit () incorporada):
def isInt_loop(v):
v = str(v).strip()
# swapping '0123456789' for '9876543210' makes nominal difference (might have because '1' is toward the beginning of the string)
numbers = '0123456789'
for i in v:
if i not in numbers:
return False
return True
def isInt_Digit(v):
v = str(v).strip()
return v.isdigit()
y supera significativamente los tiempos del resto:
timings..
isInt_try: 0.4628
isInt_str: 0.3556
isInt_re: 0.4889
isInt_re2: 0.2726
isInt_loop: 0.1842
isInt_Digit: 0.1577
usando el pitón normal 2.7:
$ python --version
Python 2.7.10
Los dos casos de prueba que agregué (isInt_loop y isInt_digit) pasan exactamente los mismos casos de prueba (ambos solo aceptan enteros sin signo), pero pensé que las personas podrían ser más inteligentes al modificar la implementación de la cadena (isInt_loop) en lugar del isdigit incorporado (), así que lo incluí, aunque haya una ligera diferencia en el tiempo de ejecución. (y ambos métodos superan mucho a todo lo demás, pero no manejan las cosas adicionales: "./+/-")
Además, me pareció interesante observar que la expresión regular (método isInt_re2) superó la comparación de cadenas en la misma prueba que realizó Shavais en 2012 (actualmente 2018). ¿Quizás las bibliotecas de expresiones regulares han sido mejoradas?
Esta es probablemente la forma más directa y pitónica de abordarlo en mi opinión. No vi esta solución y es básicamente la misma que la expresión regular, pero sin la expresión regular.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
si saltamos '-+ '
al principio y .0
, E-1
al final.
Aquí hay una función que analiza sin generar errores. Maneja retornos de casos obvios en caso None
de falla (maneja hasta 2000 signos '- / +' por defecto en CPython):
#!/usr/bin/env python
def get_int(number):
splits = number.split('.')
if len(splits) > 2:
# too many splits
return None
if len(splits) == 2 and splits[1]:
# handle decimal part recursively :-)
if get_int(splits[1]) != 0:
return None
int_part = splits[0].lstrip("+")
if int_part.startswith('-'):
# handle minus sign recursively :-)
return get_int(int_part[1:]) * -1
# successful 'and' returns last truth-y value (cast is always valid)
return int_part.isdigit() and int(int_part)
Algunas pruebas:
tests = ["0", "0.0", "0.1", "1", "1.1", "1.0", "-1", "-1.1", "-1.0", "-0", "--0", "---3", '.3', '--3.', "+13", "+-1.00", "--+123", "-0.000"]
for t in tests:
print "get_int(%s) = %s" % (t, get_int(str(t)))
Resultados:
get_int(0) = 0
get_int(0.0) = 0
get_int(0.1) = None
get_int(1) = 1
get_int(1.1) = None
get_int(1.0) = 1
get_int(-1) = -1
get_int(-1.1) = None
get_int(-1.0) = -1
get_int(-0) = 0
get_int(--0) = 0
get_int(---3) = -3
get_int(.3) = None
get_int(--3.) = 3
get_int(+13) = 13
get_int(+-1.00) = -1
get_int(--+123) = 123
get_int(-0.000) = 0
Para sus necesidades puede usar:
def int_predicate(number):
return get_int(number) is not None
Sugiero lo siguiente:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
De los documentos :
Evalúe de forma segura un nodo de expresión o una cadena que contenga un literal de Python o una pantalla de contenedor. La cadena o nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, bytes, números, tuplas, listas, dictos, conjuntos, booleanos y Ninguno.
Debo señalar que esto generará una ValueError
excepción cuando se invoque contra cualquier cosa que no constituya un literal de Python. Como la pregunta pedía una solución sin probar / excepto, tengo una solución de tipo Kobayashi-Maru para eso:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
Supongo que la pregunta está relacionada con la velocidad ya que el try / except tiene una penalización de tiempo:
Primero, creé una lista de 200 cadenas, 100 cadenas defectuosas y 100 cadenas numéricas.
from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)
np.core.defchararray.isnumeric también puede funcionar con cadenas unicode np.core.defchararray.isnumeric(u'+12')
pero devuelve una matriz. Por lo tanto, es una buena solución si tiene que hacer miles de conversiones y le faltan datos o datos no numéricos.
import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop
def check_num(s):
try:
int(s)
return True
except:
return False
def check_list(l):
return [check_num(e) for e in l]
%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop
Parece que la solución numpy es mucho más rápida.
Si solo desea aceptar dígitos con un ascii más bajo, aquí hay pruebas para hacerlo:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3.6: (u.isdecimal() and u == str(int(u)))
Otras respuestas sugieren usar .isdigit()
o .isdecimal()
pero ambos incluyen algunos caracteres unicode superiores como '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Uh .. Prueba esto:
def int_check(a):
if int(a) == a:
return True
else:
return False
Esto funciona si no pones una cadena que no sea un número.
Y también (olvidé poner la parte de verificación de número), hay una función que verifica si la cadena es un número o no. Es str.isdigit (). Aquí hay un ejemplo:
a = 2
a.isdigit()
Si llama a a.isdigit (), devolverá True.
2
asignado a
.