C'è un modo per dire se una stringa rappresenta un numero intero (es. '3'
, '-17'
Ma no '3.14'
o 'asfasfas'
) Senza usare un meccanismo try / tranne?
is_int('3.14') = False
is_int('-7') = True
C'è un modo per dire se una stringa rappresenta un numero intero (es. '3'
, '-17'
Ma no '3.14'
o 'asfasfas'
) Senza usare un meccanismo try / tranne?
is_int('3.14') = False
is_int('-7') = True
Risposte:
Se sei davvero seccato di usare try/except
s dappertutto, ti preghiamo di scrivere una funzione di supporto:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Sarà MODO più codice per coprire esattamente tutte le stringhe che Python considera numeri interi. Dico solo essere pitone su questo.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
con numeri interi positivi puoi usare .isdigit
:
>>> '16'.isdigit()
True
tuttavia non funziona con numeri interi negativi. supponiamo di poter provare quanto segue:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
non funzionerà con il '16.0'
formato, che è simile al int
casting in questo senso.
modifica :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
è vero ma int(u'²')
genera ValueError. Usa u.isdecimal()
invece. str.isdigit()
dipende dalle impostazioni locali di Python 2.
check_int('')
solleverà un'eccezione invece di tornareFalse
Sai, ho scoperto (e l'ho provato più volte) che provare / tranne non funziona così bene, per qualsiasi motivo. Provo spesso diversi modi di fare le cose, e non credo di aver mai trovato un metodo che usi try / tranne per eseguire il meglio di quelli testati, in effetti mi sembra che quei metodi di solito si siano avvicinati al peggio, se non il peggio. Non in tutti i casi, ma in molti casi. So che molte persone dicono che è il modo "Pythonic", ma questa è un'area in cui mi separo da loro. Per me, non è né molto performante né molto elegante, quindi, tendo a usarlo solo per il trapping e la segnalazione degli errori.
Avevo intenzione di lamentarmi del fatto che PHP, perl, ruby, C e persino il guscio impazzito hanno funzioni semplici per testare una stringa per un intero, ma la dovuta diligenza nel verificare quei presupposti mi ha fatto scattare! Apparentemente questa mancanza è una malattia comune.
Ecco una modifica veloce e sporca del post di 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))
Ecco i risultati del confronto delle prestazioni:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
Il metodo AC potrebbe scansionarlo una volta attraverso ed essere fatto. Penso che il metodo AC che scansiona una volta la stringa sia la cosa giusta da fare.
MODIFICARE:
Ho aggiornato il codice sopra per funzionare in Python 3.5 e per includere la funzione check_int dalla risposta attualmente più votata e per usare l'attuale regex più popolare che posso trovare per i test per integer-hood. Questa regex rifiuta stringhe come 'abc 123'. Ho aggiunto "abc 123" come valore di prova.
È molto interessante per me notare, a questo punto, che NESSUNA delle funzioni testate, incluso il metodo try, la popolare funzione check_int e la regex più popolare per i test per integer-hood, restituiscono le risposte corrette per tutte le valori di test (bene, a seconda di cosa pensi siano le risposte corrette; vedi i risultati del test di seguito).
La funzione integrata int () tronca silenziosamente la parte frazionaria di un numero in virgola mobile e restituisce la parte intera prima del decimale, a meno che il numero in virgola mobile non venga prima convertito in una stringa.
La funzione check_int () restituisce false per valori come 0.0 e 1.0 (che tecnicamente sono numeri interi) e restituisce true per valori come '06'.
Ecco i risultati del test corrente (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 |
Proprio ora ho provato ad aggiungere questa funzione:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Si comporta quasi oltre a check_int (0.3486) e restituisce true per valori come 1.0 e 0.0 e +1.0 e 0. e .0 e così via. Ma ritorna vero anche per '06', quindi. Scegli il tuo veleno, immagino.
try
è più efficiente: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
dovrebbe fare il trucco.
Esempi:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Come sottolineato da @BuzzMoschetti, in questo modo fallirà per il numero meno (ad esempio, "-23" ). Nel caso in cui input_num possa essere inferiore a 0, utilizzare re.sub (regex_search, regex_replace, contents) prima di applicare str.isdigit () . Per esempio:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Usa un'espressione regolare:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Se devi accettare anche le frazioni decimali:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Per prestazioni migliori se lo fai spesso, compila l'espressione regolare solo una volta usando re.compile()
.
La soluzione RegEx corretta combinerebbe le idee di Greg Hewgill e Nowell, ma non userebbe una variabile globale. È possibile ottenere ciò allegando un attributo al metodo. Inoltre, so che è disapprovato mettere le importazioni in un metodo, ma quello che sto cercando è un effetto "modulo pigro" come http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
modifica: La mia tecnica preferita finora è usare esclusivamente metodi dell'oggetto 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))
E per i membri meno avventurosi della classe, ecco l'output:
I compile only once. Remove this line when you are confident in that.
0 is an integer is an integre
1 is an integer is an integre
-1 is an integer is an integre
1.0 is an integer is an integre
-1.0 is an integer is an integre
'0' is an integer is an integre
'0.' is an integer is an integre
'0.0' is an integer is an integre
'1' is an integer is an integre
'-1' is an integer is an integre
'+1' is an integer is an integre
'1.0' is an integer is an integre
'-1.0' is an integer is an integre
'+1.0' is an integer is an integre
1.1 is NOT an integer is NOT an integre
-1.1 is NOT an integer is NOT an integre
'1.1' is NOT an integer is NOT an integre
'-1.1' is NOT an integer is NOT an integre
'+1.1' is NOT an integer is NOT an integre
'1.1.1' is NOT an integer is NOT an integre
'1.1.0' is NOT an integer is NOT an integre
'1.0.1' is NOT an integer is NOT an integre
'1.0.0' is NOT an integer is NOT an integre
'1.0.' is NOT an integer is NOT an integre
'1..0' is NOT an integer is NOT an integre
'1..' is NOT an integer is NOT an integre
'0.0.' is NOT an integer is NOT an integre
'0..0' is NOT an integer is NOT an integre
'0..' is NOT an integer is NOT an integre
'one' is NOT an integer is NOT an integre
<object object at 0x103b7d0a0> is NOT an integer is NOT an integre
(1, 2, 3) is NOT an integer is NOT an integre
[1, 2, 3] is NOT an integer is NOT an integre
{'one': 'two'} is NOT an integer is NOT an integre
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False
Quindi la tua funzione sarebbe:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
L'approccio di Greg Hewgill mancava di alcuni componenti: il principale "^" per abbinare solo l'inizio della stringa e compilare in anticipo il re. Ma questo approccio ti permetterà di evitare un tentativo: exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Sarei interessato al motivo per cui stai cercando di evitare di provare: tranne?
Devo farlo tutto il tempo, e ho una leggera, ma dichiaratamente irrazionale, intenzione di usare il modello try / tranne. Io lo uso questo:
all([xi in '1234567890' for xi in x])
Non contiene numeri negativi, quindi è possibile eliminare un segno meno (se presente), quindi verificare se il risultato comprende cifre comprese tra 0 e 9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Puoi anche passare x a str () se non sei sicuro che l'input sia una stringa:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Ci sono almeno due casi (limite?) In cui questo cade a pezzi:
type(1E2)
dà <class 'float'>
, mentre type(10^2)
dà <class 'int'>
.Quindi non funzionerà per ogni possibile input, ma se è possibile escludere notazione scientifica, notazione esponenziale e stringhe vuote, è un controllo su una riga OK che restituisce False
se x non è un numero intero e True
se x è un numero intero.
Non so se sia pythonic, ma è una riga ed è relativamente chiaro cosa fa il codice.
all(xi in '1234567890' for xi in x])
modello sembra più come chiedere il permesso di attraversare il prato. Non sono elettrizzato all'idea di chiedere l'autorizzazione, ma eccoci qui.
penso
s.startswith('-') and s[1:].isdigit()
sarebbe meglio riscrivere a:
s.replace('-', '').isdigit()
poiché s [1:] crea anche una nuova stringa
Ma la soluzione molto migliore è
s.lstrip('+-').isdigit()
replace
fa? Inoltre, questo accetterà in modo errato 5-2
, ad esempio.
s='-'
Mi è piaciuto molto il post di Shavais, ma ho aggiunto un altro test case (e la funzione integrata isdigit ()):
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()
e batte significativamente i tempi del resto in modo significativo:
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 il normale 2.7 python:
$ python --version
Python 2.7.10
Entrambi i due casi di test che ho aggiunto (isInt_loop e isInt_digit) superano gli stessi casi di test esatti (entrambi accettano solo numeri interi senza segno), ma ho pensato che le persone potrebbero essere più intelligenti nel modificare l'implementazione della stringa (isInt_loop) al contrario dell'isdigit integrato (), quindi l'ho inclusa, anche se c'è una leggera differenza nei tempi di esecuzione. (ed entrambi i metodi battono tutto il resto di molto, ma non gestiscono le cose extra: "./+/-")
Inoltre, ho trovato interessante notare che il regex (metodo isInt_re2) ha battuto il confronto delle stringhe nello stesso test eseguito da Shavais nel 2012 (attualmente 2018). Forse le librerie regex sono state migliorate?
Questo è probabilmente il modo più semplice e pitonico per affrontarlo secondo me. Non ho visto questa soluzione ed è sostanzialmente la stessa di regex, ma senza regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
se '-+ '
saltiamo all'inizio e .0
, E-1
alla fine.
Ecco una funzione che analizza senza generare errori. Gestisce i casi ovvi restituisce None
in caso di errore (gestisce fino a 2000 segni "- / +" per impostazione predefinita su 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)
Alcuni test:
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)))
risultati:
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
Per le tue esigenze puoi utilizzare:
def int_predicate(number):
return get_int(number) is not None
Suggerisco quanto segue:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
Dai documenti :
Valuta in modo sicuro un nodo di espressione o una stringa contenente un display letterale o contenitore Python. La stringa o il nodo forniti possono essere costituiti solo dalle seguenti strutture letterali Python: stringhe, byte, numeri, tuple, elenchi, dadi, set, valori booleani e Nessuno.
Dovrei notare che questo solleverà ValueError
un'eccezione quando chiamato contro qualsiasi cosa che non costituisca un letterale Python. Poiché la domanda ha richiesto una soluzione senza provare / tranne, ho una soluzione di tipo Kobayashi-Maru per questo:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
Immagino che la domanda sia correlata alla velocità poiché il tentativo / tranne ha una penalità di tempo:
Innanzitutto, ho creato un elenco di 200 stringhe, 100 stringhe non riuscite e 100 stringhe numeriche.
from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)
np.core.defchararray.isnumeric può anche funzionare con stringhe unicode np.core.defchararray.isnumeric(u'+12')
ma restituisce e array. Quindi, è una buona soluzione se devi fare migliaia di conversioni e hai dati mancanti o dati non numerici.
import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop
def check_num(s):
try:
int(s)
return True
except:
return False
def check_list(l):
return [check_num(e) for e in l]
%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop
Sembra che la soluzione insensibile sia molto più veloce.
Se si desidera accettare solo cifre inferiori a ASCII, ecco i test per farlo:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3.6: (u.isdecimal() and u == str(int(u)))
Altre risposte suggeriscono di usare .isdigit()
o .isdecimal()
ma entrambe includono alcuni caratteri unicode superiori come '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Uh .. Prova questo:
def int_check(a):
if int(a) == a:
return True
else:
return False
Funziona se non inserisci una stringa che non è un numero.
Inoltre (ho dimenticato di mettere la parte di controllo del numero), c'è una funzione che controlla se la stringa è un numero o no. È str.isdigit (). Ecco un esempio:
a = 2
a.isdigit()
Se chiami a.isdigit (), restituirà True.
2
assegnato a a
.