¿Cómo puedo verificar si una cadena representa un int, sin usar try / except?

467

¿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
Adam Matan
fuente
23
¿Por qué ambos intentan hacer esto "de la manera difícil"? ¿Qué hay de malo en probar / excepto?
S.Lott
55
Sí, ¿qué hay de malo en probar / excepto? Es mejor pedir perdón que permiso.
mk12
53
Me preguntaría por qué esta simple cosa requiere probar / excepto. El sistema de excepción es una bestia compleja, pero este es un problema simple.
Aivar
13
@Aivar deja de difundir FUD. Un solo bloque try / except ni siquiera se aproxima a "complejo".
Tríptico
47
Sin embargo, no es realmente FUD. Estaría escribiendo efectivamente 4 líneas de código, esperando que explote algo, detectando esa excepción y haciendo lo predeterminado, en lugar de usar un solo trazo.
andersonvom

Respuestas:

398

Si realmente está molesto por usar try/excepts 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.

Tríptico
fuente
124
Entonces, ¿es pitónico resolver un problema simple con un mecanismo complejo? Existe un algoritmo para detectar la función interna escrita "int" de int. No veo por qué no se expone esto como una función booleana.
Aivar
79
@Aivar: esta función de 5 líneas no es un mecanismo complejo.
Tríptico
34
Excepto:>>> print RepresentsInt(10.0) True >>> print RepresentsInt(10.06) True
Dannid
55
Supongo que es "pitónico" en el sentido de que si Python piensa que la cadena es un int, también lo hace su programa. Si Python cambia, también lo hace su programa, y ​​sin cambiar una sola línea de código. Hay algo de valor en eso. Puede ser lo correcto según las circunstancias.
Shavais
57
No sé por qué esta es la respuesta aceptada o tiene tantos votos positivos, ya que esto es exactamente lo contrario de lo que está pidiendo OP.
FearlessFuture
757

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 intcasting en este sentido.

editar :

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()
SilentGhost
fuente
66
esto no maneja "+17" sin un caso especial adicional.
Bryan Oakley
1
Debe probar AMBOS casos: lambda s: s.isdigit () o (s.startswith ('-') y s [1:]. Isdigit ())
robar el
44
@Roberto: ¡por supuesto que deberías! ¡y estoy seguro de que eres capaz de hacerlo!
SilentGhost
22
nota: 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.
jfs
44
check_int('')False
provocará
97

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.

Shavais
fuente
Quizás parte de esto proviene del hecho de que un entero es un poco arbitrario en sí mismo. Un sistema de programación no puede darse el lujo de asumir que siempre será una representación decimal. 0x4df, es un número entero válido en algunos lugares, y 0891 no está en otros. Me da miedo pensar qué podría surgir dado unicode en este tipo de controles.
PlexQ
3
+1 para el tiempo. Estoy de acuerdo en que todo este asunto de excepción no es realmente elegante para una pregunta tan simple. Esperarías un método auxiliar de construcción para un problema tan común ...
RickyA
99
Sé que este hilo está básicamente inactivo, pero +1 por considerar el tiempo de ejecución. La longitud de la línea no siempre es indicativa de la complejidad subyacente; y seguro, un try / except puede parecer simple (y fácil de leer, que también es importante), pero es una operación costosa. Yo diría que la jerarquía de preferencias siempre debería tener un aspecto similar al siguiente: 1. Una solución explícita fácil de leer (SilentGhost's). 2. Una solución implícita fácil de leer (tríptico). 3. No hay tres.
Eric Humphrey
1
Gracias por sus investigaciones exhaustivas sobre un tema aparentemente insignificante. Iré con isInt_str (), pythonic o no. Lo que me molesta es que no he encontrado nada sobre el significado de v.find ('..'). ¿Es algún tipo de sintaxis de búsqueda especial o un caso de borde de una cadena numérica?
JackLeEmmerdeur
3
Sí, un poco anticuado pero sigue siendo un análisis realmente agradable y relevante. En Python 3.5 tryes más eficiente: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
Dave
40

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
Catbuilts
fuente
1
Porque -23 produce falso.
Buzz Moschetti
1
@ BuzzMoschetti tienes razón. Una forma rápida de solucionarlo es eliminar el signo menos mediante re.replace (regex_search, regex_replace, contenido) antes de aplicar str.isdigit ()
Catbuilts
27

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().

Greg Hewgill
fuente
19
+1: revela que esto es horriblemente complejo y costoso en comparación con try / except.
S.Lott
2
Creo que esta es esencialmente una versión más lenta y personalizada de la solución 'isnumeric' que ofrece @SilentGhost.
Greg
@Greg: Dado que @SilentGhost no cubre los signos correctamente, esta versión realmente funciona.
S.Lott
1
@ S.Lott: seguramente, cualquier persona capaz de publicar en SO, podría extender mi ejemplo para cubrir carteles.
SilentGhost
2
las expresiones regulares son sobre lo más complejo y oscuro que existe, encuentro que la simple verificación anterior es sustancialmente más clara, incluso si creo que todavía es feo, esto es más feo.
PlexQ
18

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
Bruno Bronosky
fuente
44
Estoy de acuerdo en que mi conjunto de pruebas es excesivo. Me gusta demostrar que mi código funciona cuando lo escribo. ¿Pero crees que mi función isInteger es exagerada? Seguramente no.
Bruno Bronosky
1
Acabo de recibir un voto negativo sin comentarios. ¿Qué pasa con la gente? Entiendo que los millennials ahora usan "Me gusta" como "recibos de lectura". ¿Pero ahora están usando los votos negativos como marcadores "no es el método que elegí"? Tal vez no se dan cuenta de que resta 2 puntos de SU PROPIA reputación para rechazar una respuesta. SO / SE hace eso para alentar la votación negativa solo debido a información errónea, en cuyo caso espero que dejes un comentario .
Bruno Bronosky
5
>>> "+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()
alkos333
fuente
1
is_int ("2") genera IndexError.
anttikoo
4

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?

Nowell
fuente
1
Una cuestión de estilo. Creo que "probar / excepto" debería usarse solo con errores reales, no con el flujo normal del programa.
Adam Matan
2
@Udi Pasmon: Python hace un uso bastante intenso de try / except para el flujo de programa "normal". Por ejemplo, cada iterador se detiene con una excepción planteada.
S.Lott
3
-1: Aunque su sugerencia para compilar la expresión regular es correcta, está equivocado al criticar a Greg en el otro sentido: re.match coincide con el inicio de la cadena, por lo que el ^ en el patrón es realmente redundante. (Esto es diferente cuando usas re.search).
ThomasH
S.Lott: ¿se considera un flujo razonable en Python? ¿Cómo difiere esto de otros idiomas? Quizás valga una pregunta por separado.
Adam Matan
1
El uso intensivo de Python de try / except se ha cubierto aquí en SO. Intente buscar '[python] excepto'
S.Lott
4

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:

  1. No funciona para varias notaciones científicas y / o exponenciales (por ejemplo, 1.2E3, 10 ^ 3, etc.); ambas devolverán False. Tampoco creo que otras respuestas acomodaran esto, e incluso Python 3.8 tiene opiniones inconsistentes, ya que type(1E2)da <class 'float'>mientras type(10^2)da <class 'int'>.
  2. Una entrada de cadena vacía da True.

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 Falsesi x no es un número entero y Truesi 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.

mRotten
fuente
Intentar / excepto parece caminar sobre el césped de alguien (intentar) y luego si / cuando se dan cuenta y se enojan (excepción) te disculpas (manejas la excepción), mientras que mi 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.
mRotten
3

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()
Vladyslav Savchenko
fuente
3
¿Adivina qué replacehace? Además, esto aceptará incorrectamente 5-2, por ejemplo.
Ry-
Lanzará un IndexError sis='-'
Anti Earth
s = '-'; s.replace ('-', '') .isdigit () -> False
Vladyslav Savchenko
2

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?

brw59
fuente
1

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))
Xenlyte
fuente
set(input_string) == set(string.digits)si saltamos '-+ 'al principio y .0, E-1al final.
jfs
1

Aquí hay una función que analiza sin generar errores. Maneja retornos de casos obvios en caso Nonede 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
Reut Sharabani
fuente
1

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

¯ \ _ (ツ) _ / ¯

Jesko Hüttenhain
fuente
0

Tengo una posibilidad que no usa int en absoluto, y no debería generar una excepción a menos que la cadena no represente un número

float(number)==float(number)//1

Debería funcionar para cualquier tipo de cadena que acepte flotante, positiva, negativa, notación de ingeniería ...

agomcas
fuente
0

Supongo que la pregunta está relacionada con la velocidad ya que el try / except tiene una penalización de tiempo:

 datos de prueba

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)

 solución numpy (solo funciona con matrices y unicode)

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

probar / excepto

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.

Carlos Vega
fuente
0

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
krubo
fuente
Esto no manejará valores negativos o valores rellenados con espacios en blanco, los cuales se manejan perfectamente int().
ShadowRanger
-6

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.

HaulCozen
fuente
Creo que necesita citas alrededor del valor 2asignado a.
Luke Woodward el
1
¿Por qué no es esta la mejor respuesta? Responde la pregunta exactamente.
saltamontes
66
-1 la pregunta: "Comprueba si una cadena representa un int, sin usar Try / Except?" para @Caroline Alexiou
jfs