Comment puis-je vérifier si une chaîne représente un entier, sans utiliser try / except?


467

Existe-t-il un moyen de savoir si une chaîne représente un entier (par exemple '3', '-17'mais pas '3.14'ou 'asfasfas') sans utiliser de mécanisme try / except?

is_int('3.14') = False
is_int('-7')   = True

23
Pourquoi tous les deux essayent-ils de faire "la dure?" Quel est le problème avec try / except?
S.Lott

5
Oui, qu'est-ce qui ne va pas avec try / except? Mieux vaut demander le pardon que la permission.
mk12

53
Je demanderais pourquoi cette chose simple devrait-elle être essayée / exceptée? Le système d'exception est une bête complexe, mais c'est un problème simple.
Aivar

13
@Aivar arrête de répandre le FUD. Un seul bloc try / except n'approche même pas du "complexe".
Triptyque

47
Ce n'est pas vraiment du FUD. Vous seriez en train d'écrire 4 lignes de code, en attendant que quelque chose explose, en saisissant cette exception et en faisant votre défaut, au lieu d'utiliser une seule ligne.
andersonvom

Réponses:


398

Si vous êtes vraiment ennuyé d'utiliser try/excepts partout, veuillez simplement écrire une fonction d'aide:

def RepresentsInt(s):
    try: 
        int(s)
        return True
    except ValueError:
        return False

>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False

Cela va être BEAUCOUP plus de code pour couvrir exactement toutes les chaînes que Python considère comme des entiers. Je dis juste être pythonique sur celui-ci.


124
Il est donc pythonique de résoudre un problème simple avec un mécanisme complexe? Il existe un algorithme pour détecter la fonction interne écrite int "int" - je ne vois pas pourquoi cela n'est pas exposé comme une fonction booléenne.
Aivar

79
@Aivar: Cette fonction à 5 lignes n'est pas un mécanisme complexe.
Triptyque

34
Sauf:>>> print RepresentsInt(10.0) True >>> print RepresentsInt(10.06) True
Dannid

5
Je suppose que c'est "pythonique" dans le sens où si Python pense que la chaîne est un entier, votre programme aussi. Si Python change, votre programme aussi, et sans changer une seule ligne de code. Cela a une certaine valeur. Ce pourrait être la bonne chose à faire selon les circonstances.
Shavais

57
Je ne sais pas pourquoi c'est la réponse acceptée ou a autant de votes positifs, car c'est exactement le contraire de ce que OP demande.
FearlessFuture

757

avec des entiers positifs, vous pouvez utiliser .isdigit:

>>> '16'.isdigit()
True

cela ne fonctionne pas avec des entiers négatifs. supposons que vous puissiez essayer ce qui suit:

>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True

cela ne fonctionnera pas avec le '16.0'format, qui est similaire au intcasting dans ce sens.

modifier :

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()

6
cela ne gère pas "+17" sans un cas spécial supplémentaire.
Bryan Oakley

1
Vous devez tester les DEUX cas: lambda s: s.isdigit () ou (s.startswith ('-') et s [1:]. Isdigit ())
rob

4
@Roberto: bien sûr que vous devriez! et je suis sûr que vous en êtes capable!
SilentGhost

22
note: u'²'.isdigit()est vrai mais int(u'²')déclenche ValueError. Utilisez u.isdecimal()plutôt. str.isdigit()dépend de l'environnement local sur Python 2.
jfs

4
check_int('')False
lèvera

97

Vous savez, j'ai trouvé (et je l'ai testé maintes et maintes fois) que try / except ne fonctionne pas très bien, quelle qu'en soit la raison. J'essaie fréquemment plusieurs façons de faire les choses, et je ne pense pas avoir jamais trouvé de méthode qui utilise try / except pour exécuter les meilleures de celles testées, en fait, il me semble que ces méthodes sont généralement proches de la le pire, sinon le pire. Pas dans tous les cas, mais dans de nombreux cas. Je sais que beaucoup de gens disent que c'est la voie "Pythonique", mais c'est un domaine où je me sépare d'eux. Pour moi, ce n'est ni très performant ni très élégant, donc j'ai tendance à ne l'utiliser que pour le piégeage d'erreurs et la génération de rapports.

J'allais reprocher que PHP, perl, ruby, C et même le shell flippant ont des fonctions simples pour tester une chaîne pour le nombre entier, mais la diligence raisonnable dans la vérification de ces hypothèses m'a fait trébucher! Apparemment, ce manque est une maladie courante.

Voici une modification rapide et sale du message 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))

Voici les résultats de la comparaison des performances:

timings..
isInt_try:   0.6426
isInt_str:   0.7382
isInt_re:    1.1156
isInt_re2:   0.5344
check_int:   0.3452

La méthode AC pourrait le scanner une fois et être terminée. La méthode AC qui balaye une fois la chaîne serait la bonne chose à faire, je pense.

ÉDITER:

J'ai mis à jour le code ci-dessus pour fonctionner en Python 3.5, et pour inclure la fonction check_int à partir de la réponse actuellement la plus votée, et pour utiliser l'expression régulière la plus populaire que je puisse trouver pour tester le capotage entier. Cette expression régulière rejette les chaînes comme «abc 123». J'ai ajouté «abc 123» comme valeur de test.

Il est très intéressant pour moi de noter, à ce stade, que AUCUNE des fonctions testées, y compris la méthode try, la fonction check_int populaire, et l'expression rationnelle la plus populaire pour tester le nombre entier, renvoie les réponses correctes pour tous les valeurs du test (enfin, selon ce que vous pensez être les bonnes réponses; voir les résultats du test ci-dessous).

La fonction int () intégrée tronque silencieusement la partie fractionnaire d'un nombre à virgule flottante et renvoie la partie entière avant la décimale, sauf si le nombre à virgule flottante est d'abord converti en chaîne.

La fonction check_int () retourne false pour des valeurs comme 0.0 et 1.0 (qui sont techniquement des entiers) et retourne true pour des valeurs comme '06'.

Voici les résultats des tests actuels (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   |

Tout à l'heure, j'ai essayé d'ajouter cette fonction:

def isInt_float(s):
    try:
        return float(str(s)).is_integer()
    except:
        return False

Il fonctionne presque aussi bien que check_int (0.3486) et il retourne vrai pour des valeurs comme 1.0 et 0.0 et +1.0 et 0. et .0 et ainsi de suite. Mais cela renvoie également vrai pour '06', donc. Choisissez votre poison, je suppose.


Cela tient peut-être en partie au fait qu'un entier est lui-même un peu arbitraire. Un système de programmation ne peut pas se permettre le luxe de supposer que ce sera toujours une représentation décimale. 0x4df, est un entier valide à certains endroits, et 0891 n'est pas à d'autres. Je redoute de penser à ce qui pourrait survenir étant donné l'unicode dans ce genre de chèques.
PlexQ

3
+1 pour le timing. Je suis d'accord que toute cette affaire d'exception n'est pas vraiment élégante pour une question aussi simple. Vous vous attendriez à une méthode d'aide
intégrée

9
Je sais que ce thread est essentiellement dormant, mais +1 pour considérer l'exécution. La longueur de ligne n'est pas toujours indicative de la complexité sous-jacente; et bien sûr, un essai / sauf force simple regard (et facile à lire, ce qui est important aussi), mais il est une opération coûteuse. Je dirais que la hiérarchie des préférences devrait toujours ressembler à ceci: 1. Une solution explicite facile à lire (celle de SilentGhost). 2. Une solution implicite facile à lire (Triptyque). 3. Il n'y en a pas trois.
Eric Humphrey

1
Merci pour vos recherches approfondies sur un sujet aussi insignifiant. J'irai avec le isInt_str (), pythonique ou non. Ce qui me harcèle, c'est que je n'ai rien trouvé sur la signification de v.find ('..'). Est-ce une sorte de syntaxe spéciale de recherche ou un cas de bord d'une chaîne numérique?
JackLeEmmerdeur

3
Oui, une analyse un peu datée mais toujours vraiment sympa et pertinente. En Python 3.5 tryest plus efficace: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
Dave

40

str.isdigit() devrait faire l'affaire.

Exemples:

str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False

EDIT : Comme l'a souligné @BuzzMoschetti, cette méthode échouera pour le nombre moins (par exemple, "-23" ). Dans le cas où votre numéro_entrée peut être inférieur à 0, utilisez re.sub (regex_search, regex_replace, contents) avant d'appliquer str.isdigit () . Par exemple:

import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True

1
Parce que -23 donne faux.
Buzz Moschetti

1
@BuzzMoschetti vous avez raison. Un moyen rapide de corriger consiste à supprimer le signe moins par re.replace (regex_search, regex_replace, contents) avant d'appliquer str.isdigit ()
Catbuilts

27

Utilisez une expression régulière:

import re
def RepresentsInt(s):
    return re.match(r"[-+]?\d+$", s) is not None

Si vous devez également accepter des fractions décimales:

def RepresentsInt(s):
    return re.match(r"[-+]?\d+(\.0*)?$", s) is not None

Pour de meilleures performances si vous le faites souvent, compilez l'expression régulière une seule fois en utilisant re.compile().


19
+1: révèle que cela est horriblement complexe et coûteux par rapport à try / except.
S.Lott

2
Je pense qu'il s'agit essentiellement d'une version personnalisée plus lente de la solution «isnumeric» proposée par @SilentGhost.
Greg

@Greg: Étant donné que @SilentGhost ne couvre pas correctement les signes, cette version fonctionne réellement.
S.Lott

1
@ S.Lott: toute personne capable de publier sur SO pourrait sûrement étendre mon exemple pour couvrir les panneaux.
SilentGhost

2
les expressions régulières concernent la chose la plus complexe et la plus obscure qui existe, je trouve que la simple vérification ci-dessus est beaucoup plus claire, même si je pense que c'est toujours moche, c'est plus moche.
PlexQ

18

La bonne solution RegEx combinerait les idées de Greg Hewgill et Nowell, mais n'utiliserait pas de variable globale. Vous pouvez accomplir cela en attachant un attribut à la méthode. De plus, je sais qu'il est mal vu de placer les importations dans une méthode, mais ce que je veux, c'est un effet de "module paresseux" comme http://peak.telecommunity.com/DevCenter/Importing#lazy-imports

edit: Ma technique préférée jusqu'à présent est d'utiliser exclusivement les méthodes de l'objet 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))

Et pour les membres les moins aventureux de la classe, voici la sortie:

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

4
Je conviens que ma suite de tests est exagérée. J'aime prouver que mon code fonctionne quand je l'écris. Mais pensez-vous que ma fonction isInteger est exagérée? Sûrement pas.
Bruno Bronosky

1
Je viens de recevoir un vote négatif sans commentaires. Qu'en est-il des gens? Je comprends que les milléniaux utilisent maintenant les «J'aime» comme «lire les reçus». Mais utilisent-ils maintenant des votes négatifs comme marqueurs "pas la méthode que j'ai choisie"? Peut-être qu'ils ne réalisent pas que cela soustrait 2 points à VOTRE PROPRE réputation pour voter contre une réponse. SO / SE fait cela pour encourager le vote par la base uniquement en raison d'une désinformation, auquel cas j'espère que vous laisserez un commentaire .
Bruno Bronosky

5
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False

Votre fonction serait donc:

def is_int(val):
   return val[1].isdigit() and val.lstrip("-+").isdigit()

1
is_int ("2") déclenche IndexError.
anttikoo

4

L'approche de Greg Hewgill manquait de quelques composants: le "^" de tête pour ne correspondre qu'au début de la chaîne et la compilation préalable. Mais cette approche vous permettra d'éviter un essai: exept:

import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
    return INT_RE.match(str(s)) is not None

Je serais curieux de savoir pourquoi vous essayez d'éviter d'essayer: sauf?


1
Une question de style. Je pense que "try / except" ne devrait être utilisé qu'avec des erreurs réelles, pas avec un flux de programme normal.
Adam Matan

2
@Udi Pasmon: Python utilise assez lourdement try / sauf pour le flux de programme "normal". Par exemple, chaque itérateur s'arrête avec une exception déclenchée.
S.Lott

3
-1: Bien que votre indice de compilation de l'expression rationnelle soit correct, vous avez tort de critiquer Greg à l'autre égard: re.match correspond au début de la chaîne, donc le ^ dans le modèle est en fait redondant. (Ceci est différent lorsque vous utilisez re.search).
ThomasH

S.Lott - Est-ce considéré comme un flux raisonnable en python? En quoi cela diffère-t-il des autres langues? Cela vaut peut-être une question distincte.
Adam Matan

1
L'utilisation intensive de Python de try / except a été couverte ici sur SO. Essayez une recherche pour «[python] sauf»
S.Lott

4

Je dois le faire tout le temps, et j'ai une aversion douce mais certes irrationnelle à utiliser le modèle try / except. J'utilise ceci:

all([xi in '1234567890' for xi in x])

Il ne prend pas en charge les nombres négatifs, vous pouvez donc supprimer un signe moins (le cas échéant), puis vérifier si le résultat comprend des chiffres compris entre 0 et 9:

all([xi in '1234567890' for xi in x.replace('-', '', 1)])

Vous pouvez également passer x à str () si vous n'êtes pas sûr que l'entrée est une chaîne:

all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])

Il y a au moins deux cas (marginaux?) Où cela s'effondre:

  1. Cela ne fonctionne pas pour diverses notations scientifiques et / ou exponentielles (par exemple 1.2E3, 10 ^ 3, etc.) - les deux renverront False. Je ne pense pas non plus que d'autres réponses aient répondu à cela, et même Python 3.8 a des opinions incohérentes, car type(1E2)donne <class 'float'>alors que type(10^2)donne <class 'int'>.
  2. Une entrée de chaîne vide donne True.

Donc, cela ne fonctionnera pas pour toutes les entrées possibles, mais si vous pouvez exclure la notation scientifique, la notation exponentielle et les chaînes vides, c'est une vérification OK sur une ligne qui retourne Falsesi x n'est pas un entier et Truesi x est un entier.

Je ne sais pas si c'est pythonique, mais c'est une seule ligne, et c'est relativement clair ce que fait le code.


Essayer / sauf semble marcher sur la pelouse de quelqu'un (essayer), puis si / quand il le remarque et se fâche (exception), vous vous excusez (gérez l'exception), alors que mon all(xi in '1234567890' for xi in x])modèle ressemble plus à demander la permission de traverser la pelouse. Je ne suis pas ravi d'être un demandeur de permission, mais nous y voilà.
mRotten

3

je pense

s.startswith('-') and s[1:].isdigit()

il serait préférable de réécrire:

s.replace('-', '').isdigit()

car s [1:] crée également une nouvelle chaîne

Mais une bien meilleure solution est

s.lstrip('+-').isdigit()

3
Devinez quoi replace? En outre, cela acceptera incorrectement 5-2, par exemple.
Ry-

Va lancer une IndexError sis='-'
Anti Earth

s = '-'; s.replace ('-', '') .isdigit () -> False
Vladyslav Savchenko

2

J'ai vraiment aimé le post de Shavais, mais j'ai ajouté un cas de test de plus (et la fonction isdigit () intégrée):

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

et il bat de manière significative le temps du reste:

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

en utilisant un python 2.7 normal:

$ python --version
Python 2.7.10

Les deux cas de test que j'ai ajoutés (isInt_loop et isInt_digit) passent exactement les mêmes cas de test (ils n'acceptent tous les deux que des entiers non signés), mais je pensais que les gens pourraient être plus intelligents avec la modification de l'implémentation de la chaîne (isInt_loop) par opposition à l'isdigit intégré (), donc je l'ai inclus, même s'il y a une légère différence dans le temps d'exécution. (et les deux méthodes battent beaucoup le reste, mais ne gèrent pas les trucs supplémentaires: "./+/-")

De plus, j'ai trouvé intéressant de noter que l'expression régulière (méthode isInt_re2) a battu la comparaison des cordes dans le même test qui a été effectué par Shavais en 2012 (actuellement 2018). Peut-être que les bibliothèques regex ont été améliorées?


1

C'est probablement la façon la plus simple et la plus pythonique de l'aborder à mon avis. Je n'ai pas vu cette solution et c'est fondamentalement la même que la regex, mais sans la regex.

def is_int(test):
    import string
    return not (set(test) - set(string.digits))

set(input_string) == set(string.digits)si nous sauter '-+ 'au début du mois et .0, E-1à la fin.
jfs

1

Voici une fonction qui analyse sans générer d'erreurs. Il gère les cas évidents de retours en cas Noned'échec (gère jusqu'à 2000 signes '- / +' par défaut sur 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)

Quelques tests:

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

Résultats:

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

Pour vos besoins, vous pouvez utiliser:

def int_predicate(number):
     return get_int(number) is not None

1

Je suggère ce qui suit:

import ast

def is_int(s):
    return isinstance(ast.literal_eval(s), int)

De la documentation :

Évaluez en toute sécurité un nœud d'expression ou une chaîne contenant un littéral Python ou un affichage de conteneur. La chaîne ou le nœud fourni ne peut être composé que des structures littérales Python suivantes: chaînes, octets, nombres, tuples, listes, dictés, ensembles, booléens et Aucun.

Je dois noter que cela lèvera une ValueErrorexception lorsqu'il sera appelé contre tout ce qui ne constitue pas un littéral Python. Puisque la question demandait une solution sans try / except, j'ai une solution de type Kobayashi-Maru pour ça:

from ast import literal_eval
from contextlib import suppress

def is_int(s):
    with suppress(ValueError):
        return isinstance(literal_eval(s), int)
    return False

¯ \ _ (ツ) _ / ¯


0

J'ai une possibilité qui n'utilise pas du tout int et ne devrait pas déclencher d'exception à moins que la chaîne ne représente pas un nombre

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

Cela devrait fonctionner pour tout type de chaîne acceptée par float, positive, négative, notation technique ...


0

Je suppose que la question est liée à la vitesse car l'essai / sauf a une pénalité de temps:

 données de test

Tout d'abord, j'ai créé une liste de 200 chaînes, 100 chaînes défaillantes et 100 chaînes numériques.

from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)

 solution numpy (ne fonctionne qu'avec des tableaux et unicode)

np.core.defchararray.isnumeric peut également fonctionner avec des chaînes unicode np.core.defchararray.isnumeric(u'+12')mais il retourne et tableau. C'est donc une bonne solution si vous devez effectuer des milliers de conversions et qu'il manque des données ou des données non numériques.

import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop

essayer / sauf

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

Semble que la solution numpy est beaucoup plus rapide.


0

Si vous souhaitez accepter uniquement les chiffres ascii inférieurs, voici des tests pour le faire:

Python 3.7+: (u.isdecimal() and u.isascii())

Python <= 3,6: (u.isdecimal() and u == str(int(u)))

D'autres réponses suggèrent d'utiliser .isdigit()ou .isdecimal()mais ces deux incluent des caractères unicode supérieurs tels que '٢'( u'\u0662'):

u = u'\u0662'     # '٢'
u.isdigit()       # True
u.isdecimal()     # True
u.isascii()       # False (Python 3.7+ only)
u == str(int(u))  # False

Cela ne gérera pas les valeurs négatives ou les valeurs remplies d'espacement, qui sont toutes deux gérées très bien par int().
ShadowRanger

-6

Euh .. Essayez ceci:

def int_check(a):
    if int(a) == a:
        return True
    else:
        return False

Cela fonctionne si vous ne mettez pas une chaîne qui n'est pas un nombre.

Et aussi (j'ai oublié de mettre la partie de vérification des nombres.), Il y a une fonction vérifiant si la chaîne est un nombre ou non. C'est str.isdigit (). Voici un exemple:

a = 2
a.isdigit()

Si vous appelez a.isdigit (), il renverra True.


Je pense que vous avez besoin de guillemets autour de la valeur 2attribuée a.
Luke Woodward

1
Pourquoi n'est-ce pas la meilleure réponse? Cela répond exactement à la question.
sauterelle

6
-1 la question: "Vérifier si une chaîne représente un int, sans utiliser Try / Except?" pour @Caroline Alexiou
jfs
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.