Qual é a melhor maneira possível de verificar se uma string pode ser representada como um número no Python?
A função que atualmente tenho atualmente é:
def is_number(s):
try:
float(s)
return True
except ValueError:
return False
O que, não só é feio e lento, parece desajeitado. No entanto, eu não encontrei um método melhor porque chamar float
a função principal é ainda pior.
python
casting
floating-point
type-conversion
Daniel Goldberg
fonte
fonte
x = float('0.00'); if x: use_float(x);
agora possui um bug no seu código. Os valores reais são a razão pela qual essas funções geram uma exceção em vez de retornarNone
em primeiro lugar. Uma solução melhor é evitar a função de utilitário e cercar a chamada para flutuartry catch
quando você quiser usá-la.Respostas:
Eu disputaria os dois.
Um regex ou outro método de análise de string seria mais feio e mais lento.
Não tenho certeza de que algo muito possa ser mais rápido que o acima. Chama a função e retorna. O Try / Catch não apresenta muita sobrecarga, porque a exceção mais comum é detectada sem uma extensa pesquisa de quadros de pilha.
O problema é que qualquer função de conversão numérica tem dois tipos de resultados
C (como exemplo) corta isso de várias maneiras. O Python apresenta de forma clara e explícita.
Eu acho que seu código para fazer isso é perfeito.
fonte
try
cláusula, então eu iria colocar oreturn True
em umaelse
cláusula dotry
. Uma das razões é que, com o código na pergunta, se eu tivesse que revisá-lo, eu teria que verificar se a segunda declaração datry
cláusula não pode gerar um ValueError: concedido, isso não requer muito tempo ou força cerebral, mas por que usar quando não é necessário?IsNumeric()
eu termino com um try / catch ou outro empacotando um try / catch. Ughif is_number(s): x = float(x) else: // fail
é o mesmo número de linhas de código que otry: x = float(x) catch TypeError: # fail
. Essa função de utilitário é uma abstração totalmente desnecessária.Caso esteja procurando analisar números inteiros (positivos, não assinados) em vez de números flutuantes, você pode usar a
isdigit()
função para objetos de sequência.Métodos de seqüência de caracteres -
isdigit()
: Python2 , Python3Há também algo nas cadeias de caracteres Unicode, que eu não estou muito familiarizado com Unicode - É decimal / decimal
fonte
isdigit()
eint()
tem opiniões diferentes sobre o que é um número inteiro, por exemplo, para o caractere Unicodeu'\u00b9'
:u'¹'.isdigit()
isTrue
butint(u'¹')
raise ValueError.TL; DR A melhor solução é
s.replace('.','',1).isdigit()
Eu fiz alguns benchmarks comparando as diferentes abordagens
Se a sequência não for um número, o bloco de exceção será bastante lento. Mais importante, porém, o método try-except é a única abordagem que lida com as notações científicas corretamente.
A notação flutuante ".1234" não é suportada por:
- is_number_regex
A notação científica "1.000000e + 50" não é suportada por:
- is_number_regex
- is_number_repl_isdigit
A notação científica "1e50" não é suportada por:
- is_number_regex
- is_number_repl_isdigit
EDIT: Os resultados de referência
onde as seguintes funções foram testadas
fonte
s.replace('.','',1).isdigit()
) deve aparecer no início desta resposta. Em qualquer caso, deve ser o aceito. Obrigado!'1.5e-9'
ou em negativos.Há uma exceção que você pode levar em consideração: a string 'NaN'
Se você deseja que is_number retorne FALSE para 'NaN', esse código não funcionará, pois o Python o converterá em sua representação de um número que não é um número (fale sobre questões de identidade):
Caso contrário, devo agradecer o trecho de código que agora uso extensivamente. :)
G.
fonte
NaN
pode ser um bom valor para retornar (em vez deFalse
) se o texto passado não for de fato uma representação de um número. Verificá-lo é meio complicado (ofloat
tipo de Python realmente precisa de um método para ele), mas você pode usá-lo em cálculos sem produzir um erro e só precisa verificar o resultado.'inf'
. De qualquerinf
ouNaN
também pode ser prefixado com um+
ou-
e ainda ser aceito.x-1 == x
é verdadeiro para carros alegóricos grandes menores queinf
. No Python 3.2, você pode usarmath.isfinite
para testar números que não são NaN nem infinitos, ou verificar ambosmath.isnan
emath.isinf
antes disso.que tal agora:
que retornará verdadeiro somente se houver um ou não '.' na sequência de dígitos.
retornará falso
edit: acabei de ver outro comentário ... adicionar um
.replace(badstuff,'',maxnum_badstuff)
para outros casos pode ser feito. se você estiver passando sal e não condimentos arbitrários (ref: xkcd # 974 ), tudo ficará bem: Pfonte
1.234e56
(que também podem ser escritos como+1.234E+56
e várias outras variantes).re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')
deve fazer um trabalho melhor ao determinar um número (mas não todos, não estou afirmando isso). Eu não recomendo usar isso, muito melhor usar o código original do Questionador.Pode levar algum tempo para se acostumar, mas esta é a maneira pitônica de fazê-lo. Como já foi apontado, as alternativas são piores. Mas há uma outra vantagem de fazer as coisas dessa maneira: polimorfismo.
A idéia central por trás da digitação do pato é que "se ele anda e fala como um pato, então é um pato". E se você decidir que precisa da subclasse de seqüência de caracteres para poder alterar como determina se algo pode ser convertido em um flutuador? Ou, se você decidir testar completamente outro objeto? Você pode fazer essas coisas sem precisar alterar o código acima.
Outros idiomas resolvem esses problemas usando interfaces. Vou salvar a análise de qual solução é melhor para outro segmento. O ponto, porém, é que o python está decididamente no lado da digitação do pato da equação, e você provavelmente terá que se acostumar com uma sintaxe como essa se planeja fazer muita programação em Python (mas isso não significa você tem que gostar, é claro).
Outra coisa que você pode querer levar em consideração: o Python é muito rápido ao lançar e capturar exceções em comparação com muitas outras linguagens (30x mais rápido que o .Net, por exemplo). Caramba, o próprio idioma lança exceções para comunicar condições normais não-excepcionais do programa (toda vez que você usa um loop for). Portanto, não me preocuparia muito com os aspectos de desempenho desse código até você notar um problema significativo.
fonte
hasattr()
que apenas umagetattr()
chamada está envolvida em umtry/except
. Ainda assim, o tratamento de exceções é mais lento que o controle de fluxo normal, portanto, usá-lo para algo que será verdadeiro na maioria das vezes pode resultar em uma penalidade no desempenho.Atualizado depois que o Alfe apontou, você não precisa verificar se o flutuador é separado, pois os complexos manipulam os dois:
Anteriormente dito: Em alguns casos raros, você também pode precisar procurar números complexos (por exemplo, 1 + 2i), que não podem ser representados por um float:
fonte
float()
completamente o material e apenas verificar se acomplex()
chamada foi bem-sucedida. Tudo analisado porfloat()
pode ser analisado porcomplex()
.complex('(01989)')
retornará(1989+0j)
. Masfloat('(01989)')
vai falhar. Então eu acho que usarcomplex
não é uma boa ideia.Para
int
usar isto:Mas para
float
nós precisamos de alguns truques ;-). Cada número flutuante tem um ponto ...Também para números negativos basta adicionar
lstrip()
:E agora temos uma maneira universal:
fonte
1.234e56
e similares. Além disso, eu ficaria interessado em saber como você descobriria que99999999999999999999e99999999999999999999
não é um número. Tentando analisar, descobre rapidamente.Apenas imitar c #
No C #, existem duas funções diferentes que lidam com a análise de valores escalares:
float.parse ():
Nota: Se você está se perguntando por que alterei a exceção para um TypeError, aqui está a documentação .
float.try_parse ():
Nota: Você não deseja retornar o booleano 'False' porque esse ainda é um tipo de valor. Nenhum é melhor porque indica falha. Obviamente, se você quiser algo diferente, poderá alterar o parâmetro de falha para o que quiser.
Para estender o float para incluir o 'parse ()' e 'try_parse ()', você precisará monkeypatch a classe 'float' para adicionar esses métodos.
Se você deseja respeitar as funções preexistentes, o código deve ser algo como:
Nota: Eu, pessoalmente, prefiro chamá-lo de Monkey Punching porque parece que estou abusando do idioma quando faço isso, mas YMMV.
Uso:
E o grande sábio Pythonas disse à Santa Sé Sharpisus: "Qualquer coisa que você possa fazer, eu posso fazer melhor; eu posso fazer qualquer coisa melhor que você".
fonte
!
vez denot
pode ser um erro menor, mas você definitivamente não pode atribuir atributos aofloat
interno no CPython.Para cadeias de não-números,
try: except:
é realmente mais lento que expressões regulares. Para cadeias de números válidos, regex é mais lento. Portanto, o método apropriado depende da sua entrada.Se você achar que está em uma ligação de desempenho, poderá usar um novo módulo de terceiros chamado fastnumbers, que fornece uma função chamada isfloat . Divulgação completa, eu sou o autor. Eu incluí seus resultados nos horários abaixo.
Como você pode ver
try: except:
foi rápido para entrada numérica, mas muito lento para uma entrada inválidafastnumbers
ganha em ambos os casosfonte
prep_code_basis
eprep_code_re_method
tivesse evitado meu erro.isfloat
função?str(s).strip('-').replace('.','',1).isdigit()
é aproximadamente 10 vezes mais lento!Sei que isso é particularmente antigo, mas gostaria de acrescentar uma resposta que acredito que cubra as informações que faltam na resposta mais votada e que podem ser muito valiosas para quem encontrar isso:
Para cada um dos métodos a seguir, conecte-os a uma contagem, se você precisar de alguma entrada para ser aceita. (Supondo que estamos usando definições vocais de números inteiros em vez de 0 a 255, etc.)
x.isdigit()
funciona bem para verificar se x é um número inteiro.x.replace('-','').isdigit()
funciona bem para verificar se x é negativo (primeira posição do check-in)x.replace('.','').isdigit()
funciona bem para verificar se x é um decimal.x.replace(':','').isdigit()
funciona bem para verificar se x é uma proporção.x.replace('/','',1).isdigit()
funciona bem para verificar se x é uma fração.fonte
x.replace('/','',1).isdigit()
ou datas como 7/7/2017 seriam mal interpretadas como números.Esta resposta fornece guia passo a passo com função de exemplos para encontrar a string:
Verifique se a sequência é um número inteiro positivo
Você pode usar
str.isdigit()
para verificar se a sequência especificada é um número inteiro positivo .Resultados da amostra:
Verifique se a string é positiva / negativa - inteiro / flutuante
str.isdigit()
retornaFalse
se a sequência for um número negativo ou um número flutuante. Por exemplo:Se você também
float
deseja verificar os números inteiros negativos e , pode escrever uma função personalizada para verificar como:Exemplo de execução:
Descartar cadeias "NaN" (não um número) enquanto verifica o número
As funções acima retornarão
True
para a string "NAN" (não é um número) porque, para o Python, é válido float representando que não é um número. Por exemplo:Para verificar se o número é "NaN", você pode usar
math.isnan()
como:Ou, se você não deseja importar uma biblioteca adicional para verificar isso, basta checá-la através da comparação com ela mesma
==
. O Python retornaFalse
quando onan
float é comparado com ele próprio. Por exemplo:Por isso, acima de função
is_number
pode ser atualizado para voltarFalse
para"NaN"
como:Exemplo de execução:
PS: Cada operação para cada verificação, dependendo do tipo de número, é fornecida com sobrecarga adicional. Escolha a versão da
is_number
função que atenda às suas necessidades.fonte
A conversão para float e a captura de ValueError é provavelmente a maneira mais rápida, pois float () se destina especificamente a isso. Qualquer outra coisa que exija análise de string (regex, etc) provavelmente será mais lenta devido ao fato de não estar ajustado para esta operação. Meus US $ 0,02.
fonte
Você pode usar strings Unicode, eles têm um método para fazer exatamente o que você deseja:
Ou:
http://www.tutorialspoint.com/python/string_isnumeric.htm
http://docs.python.org/2/howto/unicode.html
fonte
s.isdecimal()
verifica se as
sequência é um número inteiro não negativo.s.isnumeric()
inclui caracteres queint()
rejeitam.Eu queria ver qual método é mais rápido. No geral, os melhores e mais consistentes resultados foram dados pela
check_replace
função. Os resultados mais rápidos foram dados pelacheck_exception
função, mas somente se não houver nenhuma exceção acionada - o que significa que o código é o mais eficiente, mas a sobrecarga de gerar uma exceção é bastante grande.Observe que a verificação de uma conversão bem-sucedida é o único método preciso, por exemplo, isso funciona,
check_exception
mas as outras duas funções de teste retornam False para um float válido:Aqui está o código de referência:
Aqui estão os resultados com o Python 2.7.10 em um MacBook Pro 13 de 2017:
Aqui estão os resultados com o Python 3.6.5 em um MacBook Pro 13 de 2017:
Aqui estão os resultados com o PyPy 2.7.13 em um MacBook Pro 13 de 2017:
fonte
Então, para juntar tudo, verificando Nan, infinito e números complexos (parece que eles são especificados com j, não i, ou seja, 1 + 2j), resulta em:
fonte
A entrada pode ser a seguinte:
a="50"
b=50
c=50.1
d="50.1"
Entrada 1-Geral:
A entrada desta função pode ser tudo!
Localiza se a variável especificada é numérica. Sequências numéricas consistem em sinal opcional, qualquer número de dígitos, parte decimal opcional e parte exponencial opcional. Portanto, + 0123.45e6 é um valor numérico válido. Notação hexadecimal (por exemplo, 0xf4c3b00c) e binária (por exemplo, 0b10100111001) não é permitida.
função is_numeric
teste:
função is_float
Localiza se a variável fornecida é flutuante. strings flutuantes consistem em sinal opcional, qualquer número de dígitos, ...
teste:
o que é ast ?
2- Se você tem certeza de que o conteúdo da variável é String :
use o método str.isdigit ()
Entrada numérica 3:
detectar valor int:
detectar flutuador:
fonte
ast
"?Eu fiz um teste de velocidade. Vamos dizer que, se a string provavelmente for um número, a estratégia try / except será a mais rápida possível. Se a string provavelmente não for um número e você estiver interessado na verificação de número inteiro , vale a pena fazer algum teste (isdigit mais header '-'). Se você estiver interessado em verificar o número da bóia, use o código try / except sem escape.
fonte
Eu precisava determinar se uma string era convertida em tipos básicos (float, int, str, bool). Depois de não encontrar nada na internet, criei isso:
Exemplo
Você pode capturar o tipo e usá-lo
fonte
RyanN sugere
Mas isso não funciona muito bem, porque para flutuadores suficientemente grandes,
x-1 == x
retorna verdadeiro. Por exemplo,2.0**54 - 1 == 2.0**54
fonte
Eu acho que a sua solução é bom, mas não é uma implementação regexp correta.
Parece haver muito ódio do regexp em relação a essas respostas, que eu acho injustificadas, o regexps pode ser razoavelmente limpo, correto e rápido. Realmente depende do que você está tentando fazer. A questão original era como você pode "verificar se uma string pode ser representada como um número (float)" (conforme o seu título). Presumivelmente, você desejaria usar o valor numérico / flutuante depois de verificar se ele é válido; nesse caso, sua tentativa / exceção faz muito sentido. Mas se, por algum motivo, você apenas deseja validar que uma string é um númeroentão uma regex também funciona bem, mas é difícil de corrigir. Eu acho que a maioria das respostas do regex até agora, por exemplo, não analisa corretamente as strings sem uma parte inteira (como ".7"), que é um float no que diz respeito ao python. E isso é um pouco complicado de verificar em um único regex em que a parte fracionária não é necessária. Eu incluí dois regex para mostrar isso.
Isso levanta a questão interessante sobre o que é um "número". Você inclui "inf", que é válido como flutuador em python? Ou você inclui números que são "números", mas talvez não possam ser representados em python (como números que são maiores que o número máximo de float).
Também há ambigüidades na maneira como você analisa números. Por exemplo, o que dizer de "--20"? Este é um "número"? Essa é uma maneira legal de representar "20"? O Python permitirá que você faça "var = --20" e defina-o como 20 (embora realmente seja porque o trata como uma expressão), mas o float ("- 20") não funciona.
De qualquer forma, sem mais informações, aqui está uma expressão regular que eu acredito que cobre todas as entradas e flutuações conforme o python as analisa .
Alguns exemplos de valores de teste:
A execução do código de benchmarking na resposta do @ ron-reiter mostra que esse regex é realmente mais rápido que o normal e é muito mais rápido no tratamento de valores ruins que a exceção, o que faz algum sentido. Resultados:
fonte
fonte
1e6
representar um número?Aqui está a minha maneira simples de fazer isso. Digamos que eu estou percorrendo algumas strings e quero adicioná-las a uma matriz se elas forem números.
Substitua myvar.apppend por qualquer operação que você queira executar com a string, se for um número. A idéia é tentar usar uma operação float () e usar o erro retornado para determinar se a string é ou não um número.
fonte
Também usei a função que você mencionou, mas logo noto que cadeias de caracteres como "Nan", "Inf" e sua variação são consideradas como número. Então, proponho que você versão melhorada de sua função, que retornará false nesse tipo de entrada e não falhe nas variantes "1e3":
fonte
Esse código lida com os expoentes, flutuadores e números inteiros, sem o uso de regex.
fonte
Função auxiliar do usuário:
então
fonte
Você pode generalizar a técnica de exceção de uma maneira útil, retornando valores mais úteis que True e False. Por exemplo, essa função coloca aspas em volta das seqüências, mas deixa os números sozinhos. O que é exatamente o que eu precisava para um filtro rápido e sujo para fazer algumas definições de variáveis para R.
fonte
Eu estava trabalhando em um problema que me levou a esse segmento, como converter uma coleção de dados em seqüências de caracteres e números da maneira mais intuitiva. Depois de ler o código original, percebi que o que eu precisava era diferente de duas maneiras:
1 - Eu queria um resultado inteiro se a sequência representasse um número inteiro
2 - Eu queria que um resultado de número ou string ficasse em uma estrutura de dados
então eu adaptei o código original para produzir esse derivado:
fonte
Tente isso.
fonte
is_number('10')
fonte