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?
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:
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.
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.
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.
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 =[# integers0,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:returnFalsereturnTruedef isInt_str(v):
v = str(v).strip()return v=='0'or(v if v.find('..')>-1else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()def isInt_re(v):import re
ifnot hasattr(isInt_re,'intRegex'):
isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")return isInt_re.intRegex.match(str(v).strip())isnotNonedef isInt_re2(v):return g_intRegex.match(str(v).strip())isnotNonedef 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:
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):
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.
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
@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
defRepresentsInt(s):return re.match(r"[-+]?\d+$", s)isnotNone
Si vous devez également accepter des fractions décimales:
+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 objectdef isInteger(i):
i = str(i)return i=='0'or(i if i.find('..')>-1else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()# Uses re module for regexdef isIntegre(i):import re
ifnot 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))isnotNone# When executed directly run Unit Testsif __name__ =='__main__':for obj in[# integers0,1,-1,1.0,-1.0,'0','0.','0.0','1','-1','+1','1.0','-1.0','+1.0',# non-integers1.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 outputif 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.0is an integer is an integre
1is an integer is an integre
-1is an integer is an integre
1.0is an integer is an integre
-1.0is 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.1is NOT an integer is NOT an integre
-1.1is 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
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 .
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+$")defRepresentsInt(s):return INT_RE.match(str(s))isnotNone
Je serais curieux de savoir pourquoi vous essayez d'éviter d'essayer: sauf?
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:
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'>.
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à.
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 notin numbers:returnFalsereturnTruedef isInt_Digit(v):
v = str(v).strip()return v.isdigit()
et il bat de manière significative le temps du reste:
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?
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.
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 pythondef get_int(number):
splits = number.split('.')if len(splits)>2:# too many splitsreturnNoneif len(splits)==2and splits[1]:# handle decimal part recursively :-)if get_int(splits[1])!=0:returnNone
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)))
É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)returnFalse
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)returnTrueexcept:returnFalsedef 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.
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:
Réponses:
Si vous êtes vraiment ennuyé d'utiliser
try/except
s partout, veuillez simplement écrire une fonction d'aide: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.
la source
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
avec des entiers positifs, vous pouvez utiliser
.isdigit
:cela ne fonctionne pas avec des entiers négatifs. supposons que vous puissiez essayer ce qui suit:
cela ne fonctionnera pas avec le
'16.0'
format, qui est similaire auint
casting dans ce sens.modifier :
la source
u'²'.isdigit()
est vrai maisint(u'²')
déclenche ValueError. Utilisezu.isdecimal()
plutôt.str.isdigit()
dépend de l'environnement local sur Python 2.check_int('')
False
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:
Voici les résultats de la comparaison des performances:
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):
Tout à l'heure, j'ai essayé d'ajouter cette fonction:
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.
la source
try
est plus efficace: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.str.isdigit()
devrait faire l'affaire.Exemples:
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:
la source
Utilisez une expression régulière:
Si vous devez également accepter des fractions décimales:
Pour de meilleures performances si vous le faites souvent, compilez l'expression régulière une seule fois en utilisant
re.compile()
.la source
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.
Et pour les membres les moins aventureux de la classe, voici la sortie:
la source
Votre fonction serait donc:
la source
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:
Je serais curieux de savoir pourquoi vous essayez d'éviter d'essayer: sauf?
la source
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:
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:
Vous pouvez également passer x à str () si vous n'êtes pas sûr que l'entrée est une chaîne:
Il y a au moins deux cas (marginaux?) Où cela s'effondre:
type(1E2)
donne<class 'float'>
alors quetype(10^2)
donne<class 'int'>
.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
False
si x n'est pas un entier etTrue
si 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.
la source
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à.je pense
il serait préférable de réécrire:
car s [1:] crée également une nouvelle chaîne
Mais une bien meilleure solution est
la source
replace
? En outre, cela acceptera incorrectement5-2
, par exemple.s='-'
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):
et il bat de manière significative le temps du reste:
en utilisant un python 2.7 normal:
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?
la source
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.
la source
set(input_string) == set(string.digits)
si nous sauter'-+ '
au début du mois et.0
,E-1
à la fin.Voici une fonction qui analyse sans générer d'erreurs. Il gère les cas évidents de retours en cas
None
d'échec (gère jusqu'à 2000 signes '- / +' par défaut sur CPython!):Quelques tests:
Résultats:
Pour vos besoins, vous pouvez utiliser:
la source
Je suggère ce qui suit:
De la documentation :
Je dois noter que cela lèvera une
ValueError
exception 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:¯ \ _ (ツ) _ / ¯
la source
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
Cela devrait fonctionner pour tout type de chaîne acceptée par float, positive, négative, notation technique ...
la source
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.
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.essayer / sauf
Semble que la solution numpy est beaucoup plus rapide.
la source
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'
):la source
int()
.Euh .. Essayez ceci:
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:
Si vous appelez a.isdigit (), il renverra True.
la source
2
attribuéea
.