Existe alguma maneira de saber se uma string representa um número inteiro (por exemplo '3'
, '-17'
mas não '3.14'
ou 'asfasfas'
) sem usar um mecanismo try / except?
is_int('3.14') = False
is_int('-7') = True
Existe alguma maneira de saber se uma string representa um número inteiro (por exemplo '3'
, '-17'
mas não '3.14'
ou 'asfasfas'
) sem usar um mecanismo try / except?
is_int('3.14') = False
is_int('-7') = True
Respostas:
Se você está realmente irritado com o uso de try/except
s em todo o lugar, basta escrever uma função auxiliar:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Vai ser MUITO mais código para cobrir exatamente todas as strings que o Python considera inteiros. Eu digo apenas seja pythonic neste.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
com números inteiros positivos você pode usar .isdigit
:
>>> '16'.isdigit()
True
embora não funcione com números inteiros negativos. suponha que você possa tentar o seguinte:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
não funcionará com o '16.0'
formato, que é semelhante ao int
vazamento nesse sentido.
editar :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
é verdade, mas int(u'²')
gera ValueError. Use em u.isdecimal()
vez disso. str.isdigit()
é dependente da localidade no Python 2. #
check_int('')
irá gerar uma exceção em vez de voltarFalse
Você sabe, eu descobri (e testei isso várias vezes) que tentar / exceto não funciona tão bem, por qualquer motivo. Frequentemente, tento várias maneiras de fazer as coisas e acho que nunca encontrei um método que use try / exceto para executar o melhor dos testados; na verdade, parece-me que esses métodos geralmente se aproximam do pior, se não o pior. Não em todos os casos, mas em muitos casos. Eu sei que muitas pessoas dizem que é o caminho "Pythonic", mas essa é uma área em que me separo deles. Para mim, não é muito eficiente nem muito elegante; portanto, costumo usá-lo apenas para capturar e reportar erros.
Eu ia entender que PHP, perl, ruby, C e até mesmo o maldito shell têm funções simples para testar uma string para um número inteiro, mas a devida diligência em verificar essas suposições me levou a sério! Aparentemente, essa falta é uma doença comum.
Aqui está uma edição rápida e suja da postagem 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))
Aqui estão os resultados da comparação de desempenho:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
O método AC pode digitalizá-lo uma vez e concluir. O método AC que varre a string uma vez seria a coisa certa a fazer, eu acho.
EDITAR:
Atualizei o código acima para trabalhar no Python 3.5 e incluir a função check_int da resposta mais votada no momento e para usar o regex mais popular atual que posso encontrar para testar o número inteiro. Esse regex rejeita seqüências de caracteres como 'abc 123'. Eu adicionei 'abc 123' como um valor de teste.
É muito interessante para mim observar, neste ponto, que NENHUMA das funções testadas, incluindo o método try, a popular função check_int e o regex mais popular para testar a integração de números inteiros, retornam as respostas corretas para todos os valores de teste (bem, dependendo das respostas corretas, consulte os resultados do teste abaixo).
A função interna int () silenciosamente trunca a parte fracionária de um número de ponto flutuante e retorna a parte inteira antes do decimal, a menos que o número do ponto flutuante seja primeiro convertido em uma seqüência de caracteres.
A função check_int () retorna false para valores como 0.0 e 1.0 (que tecnicamente são números inteiros) e retorna true para valores como '06'.
Aqui estão os resultados dos testes atuais (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 |
Só agora eu tentei adicionar esta função:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Ele executa quase tão bem quanto check_int (0.3486) e retorna true para valores como 1.0 e 0.0 e +1.0 e 0. e .0 e assim por diante. Mas também retorna verdadeiro para '06', então. Escolha seu veneno, eu acho.
try
é mais eficiente: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
deve fazer o truque.
Exemplos:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Como o @BuzzMoschetti apontou, esse caminho falhará no número negativo (por exemplo, "-23" ). Caso seu input_num possa ser menor que 0, use re.sub (regex_search, regex_replace, contents) antes de aplicar str.isdigit () . Por exemplo:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Use uma expressão regular:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Se você deve aceitar frações decimais também:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Para melhorar o desempenho, se você estiver fazendo isso com frequência, compile a expressão regular apenas uma vez usando re.compile()
.
A solução RegEx adequada combinaria as idéias de Greg Hewgill e Nowell, mas não usaria uma variável global. Você pode fazer isso anexando um atributo ao método Além disso, eu sei que é desagradável colocar as importações em um método, mas o que eu desejo é um efeito de "módulo lento" como http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
editar: Minha técnica favorita até agora é usar exclusivamente métodos do 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))
E para os membros menos aventureiros da classe, aqui está a saída:
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
Portanto, sua função seria:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
A abordagem de Greg Hewgill estava faltando alguns componentes: o "^" principal para corresponder apenas ao início da string e compilando o re antecipadamente. Mas essa abordagem permitirá que você evite uma tentativa: exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Eu estaria interessado em saber por que você está tentando evitar a tentativa: exceto?
Eu tenho que fazer isso o tempo todo e tenho uma aversão leve, mas reconhecidamente irracional, ao uso do padrão try / except. Eu uso isso:
all([xi in '1234567890' for xi in x])
Ele não acomoda números negativos; portanto, você pode retirar um sinal de menos (se houver) e verificar se o resultado inclui dígitos de 0 a 9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Você também pode passar x para str () se não tiver certeza de que a entrada é uma string:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Há pelo menos dois casos (edge?) Em que isso se desfaz:
type(1E2)
dá <class 'float'>
que type(10^2)
dá<class 'int'>
.Portanto, não funcionará para todas as entradas possíveis, mas se você puder excluir notação científica, notação exponencial e seqüências de caracteres vazias, é uma verificação de linha única OK que retorna False
se x não for um número inteiro eTrue
se x for um número inteiro.
Não sei se é python, mas é uma linha e é relativamente claro o que o código faz.
all(xi in '1234567890' for xi in x])
padrão parece mais pedir permissão para atravessar o gramado. Não estou emocionado por pedir permissão, mas aqui estamos.
eu acho que
s.startswith('-') and s[1:].isdigit()
seria melhor reescrever para:
s.replace('-', '').isdigit()
porque s [1:] também cria uma nova string
Mas uma solução muito melhor é
s.lstrip('+-').isdigit()
replace
faz? Além disso, isso aceitará incorretamente 5-2
, por exemplo.
s='-'
Gostei muito do post do Shavais, mas adicionei mais um caso de teste (e a função isdigit () integrada):
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 supera consistentemente os tempos do 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 python 2.7 normal:
$ python --version
Python 2.7.10
Os dois casos de teste que adicionei (isInt_loop e isInt_digit) passam exatamente nos mesmos casos de teste (ambos aceitam números inteiros não assinados), mas achei que as pessoas poderiam ser mais espertas ao modificar a implementação da string (isInt_loop) em oposição ao isdigit incorporado (), então eu a incluí, mesmo que haja uma pequena diferença no tempo de execução. (e os dois métodos superam muito o resto, mas não lidam com o material extra: "./+/-")
Além disso, achei interessante notar que o regex (método isInt_re2) superou a comparação de cadeias no mesmo teste que foi realizado pelo Shavais em 2012 (atualmente em 2018). Talvez as bibliotecas regex tenham sido melhoradas?
Esta é provavelmente a maneira mais direta e pitônica de abordá-la na minha opinião. Eu não vi essa solução e é basicamente o mesmo que o regex, mas sem o regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
se pularmos '-+ '
no começo e .0
, E-1
no final.
Aqui está uma função que analisa sem gerar erros. Ele lida com casos óbvios de retorno None
em caso de falha (lida com até 2000 sinais '- / +' por padrão no 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)
Alguns testes:
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 suas necessidades, você pode usar:
def int_predicate(number):
return get_int(number) is not None
Sugiro o seguinte:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
Dos documentos :
Avalie com segurança um nó de expressão ou uma sequência que contém um literal Python ou exibição de contêiner. A cadeia ou nó fornecido pode consistir apenas nas seguintes estruturas literais do Python: cadeias, bytes, números, tuplas, listas, dictos, conjuntos, booleanos e Nenhum.
Devo observar que isso gerará uma ValueError
exceção quando chamado contra qualquer coisa que não constitua um literal Python. Como a pergunta foi feita por uma solução sem tentativa / exceção, eu tenho uma solução do tipo Kobayashi-Maru para isso:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
Eu acho que a pergunta está relacionada à velocidade, já que o try / except tem uma penalidade de tempo:
Primeiro, criei uma lista de 200 strings, 100 strings com falha e 100 strings numéricas.
from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)
O np.core.defchararray.isnumeric também pode trabalhar com seqüências unicode, np.core.defchararray.isnumeric(u'+12')
mas ele retorna e matriz. Portanto, é uma boa solução se você tiver que fazer milhares de conversões e tiver dados ausentes ou dados não numéricos.
import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop
def check_num(s):
try:
int(s)
return True
except:
return False
def check_list(l):
return [check_num(e) for e in l]
%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop
Parece que a solução numpy é muito mais rápida.
Se você deseja aceitar apenas dígitos ASCII inferiores, aqui estão os testes para fazer isso:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3.6: (u.isdecimal() and u == str(int(u)))
Outras respostas sugerem o uso de .isdigit()
ou, .isdecimal()
mas ambos incluem alguns caracteres unicode superiores , como '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Tente isso:
def int_check(a):
if int(a) == a:
return True
else:
return False
Isso funciona se você não colocar uma string que não seja um número.
E também (eu esqueci de colocar a parte de verificação do número.), Existe uma função que verifica se a sequência é um número ou não. É str.isdigit (). Aqui está um exemplo:
a = 2
a.isdigit()
Se você chamar a.isdigit (), ele retornará True.
2
atribuído a
.