Respostas:
A declaração
if A:
chamará A.__nonzero__()
(consulte a documentação sobre nomes de métodos especiais ) e usará o valor de retorno dessa função. Aqui está o resumo:
object.__nonzero__(self)
Chamado para implementar o teste de valor de verdade e a operação interna
bool()
; deve retornarFalse
ouTrue
, ou seus equivalentes inteiros0
ou1
. Quando esse método não é definido,__len__()
é chamado, se definido, e o objeto é considerado verdadeiro se o resultado for diferente de zero. Se uma classe não define nem__len__()
nem__nonzero__()
, todas as suas instâncias são consideradas verdadeiras.
Por outro lado,
if A is not None:
compara única referência A
com None
para ver se ele é o mesmo ou não.
if object(): pass
é de ~ 0,130 usec por loop, enquanto if object() is not None: pass
é de ~ 0,135 usec. De qualquer forma, você não deve usar o desempenho para escolher entre esses dois, mas observe as diferenças em como elas funcionam, uma vez que não são equivalentes .
if A is not None
parece ser mais lento porque é uma comparação e precisa carregar o singleton interno None
como uma etapa intermediária para comparar A
(dê uma olhada no dis.dis()
). Corrija-me se estiver errado, mas if A:
parece ser mais eficiente, assim que você realmente quiser testar o valor da verdade e não a None
identidade.
python -m timeit -s"a=0" "if a: pass" "else: pass"
é mais rápido do que, python -m timeit -s"a=0" "if a is None: pass" "else: pass"
mas python -m timeit -s"a=1" "if a: pass" "else: pass"
é mais lento. Pode ser dependente da plataforma, veja se você obtém os mesmos resultados
is None
teste foi realmente o mais lento para mim. Em PyPy todos eles mediram exatamente o mesmo :)
Conforme escrito em PEP8 :
Comparações com singletons como None devem sempre ser feitas com 'is' ou 'is not', nunca com os operadores de igualdade .
Além disso, tenha cuidado ao escrever "se x" quando realmente quiser "se x não for Nenhum" - por exemplo, ao testar se uma variável ou argumento padrão como Nenhum foi definido como outro valor. O outro valor pode ter um tipo (como um contêiner) que pode ser falso em um contexto booleano!
None
, mas apenas pela verificação do valor de verdade. Nesse caso, if A:
parece mais eficiente (por exemplo dis.dis()
, a , existem etapas extras para carregar o built-in None
e comparar, com if A is not None:
, enquanto há apenas um jump_if
no outro caso).
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None
então não é o mesmo que
if x is not None # which works only on None
Muitas funções retornam None se não houver resultados apropriados. Por exemplo, uma consulta SQLAlchemy.first()
método de retorna None se não houver linhas no resultado. Suponha que você esteja selecionando um valor que possa retornar 0 e precise saber se é realmente 0 ou se a consulta não teve resultados.
Um idioma comum é fornecer ao argumento opcional de uma função ou método o valor padrão de Nenhum e, em seguida, testar esse valor como Nenhum para ver se ele foi especificado. Por exemplo:
def spam(eggs=None):
if eggs is None:
eggs = retrievefromconfigfile()
compare isso com:
def spam(eggs=None):
if not eggs:
eggs = retrievefromconfigfile()
Neste último caso, o que acontece se você ligar spam(0)
ou spam([])
? A função detectaria (incorretamente) que você não havia passado um valor paraeggs
e calcularia um valor padrão para você. Provavelmente não é isso que você deseja.
Ou imagine um método como "retornar a lista de transações para uma determinada conta". Se a conta não existir, ela poderá retornar Nenhuma. Isso é diferente de retornar uma lista vazia (o que significa "esta conta existe, mas não registrou transações).
Finalmente, de volta ao material do banco de dados. Há uma grande diferença entre NULL e uma string vazia. Uma string vazia normalmente diz "há um valor aqui e esse valor não é nada". NULL diz "este valor não foi inserido".
Em cada um desses casos, você deseja usar if A is None
. Você está verificando um valor específico - Nenhum - não apenas "qualquer valor que seja convertido em Falso".
Eles fazem coisas muito diferentes .
Os cheques abaixo se A tem nada, exceto os valores False
, []
, None
, ''
e 0
. Verifica o valor de A.
if A:
O abaixo verifica se A é um objeto diferente de Nenhum. Ele verifica e compara a referência (endereço de memória) de A e Nenhum.
if A is not None:
UPDATE: Mais explicações
Muitas vezes os dois parecem fazer a mesma coisa, então muitas pessoas os usam de forma intercambiável - é uma péssima ideia. A razão pela qual os dois dão os mesmos resultados é muitas vezes por pura coincidência devido a otimizações do intérprete / compilador, como internamento ou outra coisa.
Com essas otimizações em mente, números inteiros e seqüências de caracteres do mesmo valor acabam usando o mesmo espaço de memória. Isso provavelmente explica por que duas seqüências separadas agem como se fossem iguais.
> a = 'test'
> b = 'test'
> a is b
True
> a == b
True
Outras coisas não se comportam da mesma maneira ..
> a = []
> b = []
> a is b
False
> a == b
True
As duas listas claramente têm sua própria memória. Surpreendentemente, tuplas se comportam como cordas.
> a = ()
> b = ()
> a is b
True
> a == b
True
Provavelmente, isso porque as tuplas têm a garantia de não mudar, portanto, faz sentido reutilizar a mesma memória.
A conclusão é que você não pode confiar em coincidências . Só porque grasna como um pato, isso não significa que é um pato. Use is
e ==
dependendo do que você realmente deseja verificar. Essas coisas podem ser difíceis de depurar, uma vez que se is
lê como prosa que geralmente apenas examinamos.
None
é um singleton, não é um detalhe de implementação (ao contrário de int ou string interning). Não tenho certeza se sigo o que você quer dizer.
None
se comporte de maneira diferente int
ou str
devido à internação. Meu ponto é que is
e ==
estão verificando coisas diferentes; primeiro verifica um endereço de memória, o segundo verifica o conteúdo dos endereços de memória.
if A:
será falso se A for 0, Falso, sequência vazia, lista vazia ou Nenhum, o que pode levar a resultados indesejados.
A maioria dos guias que vi sugerem que você deveria usar
se um:
a menos que você tenha um motivo para ser mais específico.
Existem algumas pequenas diferenças. Existem valores diferentes de None que retornam False, por exemplo, listas vazias ou 0, então pense no que você está realmente testando.
Nenhum é um valor especial no Python que geralmente designa uma variável não inicializada. Para testar se A não possui esse valor específico, use:
if A is not None
Os valores de Falsey são uma classe especial de objetos em Python (por exemplo, false, []). Para testar se A é falsey, use:
if not A
Assim, as duas expressões não são as mesmas e é melhor não tratá-las como sinônimos.
PS Nenhum também é falsey, então a primeira expressão implica a segunda. Mas o segundo abrange outros valores falsey além de None. Agora ... se você tiver certeza de que não pode ter outros valores de falsey além de Nenhum em A, poderá substituir a primeira expressão pela segunda.
Depende do contexto.
Uso if A:
quando espero A
que seja algum tipo de coleção e só quero executar o bloco se a coleção não estiver vazia. Isso permite que o chamador passe qualquer coleção bem-comportada, vazia ou não, e faça com que eu faça o que eu espero. Também permite None
eFalse
suprime a execução do bloco, o que é ocasionalmente conveniente para chamar código.
OTOH, se eu espero A
que seja algum objeto completamente arbitrário, mas poderia ter sido padronizado None
, eu sempre o uso if A is not None
, pois o código de chamada pode ter passado deliberadamente uma referência a uma coleção vazia, cadeia vazia ou um tipo numérico com valor 0 ou boleanoFalse
ou alguma instância de classe que seja falsa no contexto booleano.
E, por outro lado, se eu espero A
ser algo mais específico (por exemplo, instância de uma classe da qual chamarei métodos), mas poderia ter sido o padrão None
, e considero a conversão booleana padrão uma propriedade da classe Não me importo de impor todas as subclasses, então usarei if A:
para poupar meus dedos o terrível fardo de digitar 12 caracteres extras.
Criei um arquivo chamado test.py
e o executei no intérprete. Você pode mudar o que quiser, para testar com certeza como as coisas estão acontecendo nos bastidores.
import dis
def func1():
matchesIterator = None
if matchesIterator:
print( "On if." );
def func2():
matchesIterator = None
if matchesIterator is not None:
print( "On if." );
print( "\nFunction 1" );
dis.dis(func1)
print( "\nFunction 2" );
dis.dis(func2)
Esta é a diferença do assembler:
Fonte:
>>> import importlib
>>> reload( test )
Function 1
6 0 LOAD_CONST 0 (None)
3 STORE_FAST 0 (matchesIterator)
8 6 LOAD_FAST 0 (matchesIterator)
9 POP_JUMP_IF_FALSE 20
10 12 LOAD_CONST 1 ('On if.')
15 PRINT_ITEM
16 PRINT_NEWLINE
17 JUMP_FORWARD 0 (to 20)
>> 20 LOAD_CONST 0 (None)
23 RETURN_VALUE
Function 2
14 0 LOAD_CONST 0 (None)
3 STORE_FAST 0 (matchesIterator)
16 6 LOAD_FAST 0 (matchesIterator)
9 LOAD_CONST 0 (None)
12 COMPARE_OP 9 (is not)
15 POP_JUMP_IF_FALSE 26
18 18 LOAD_CONST 1 ('On if.')
21 PRINT_ITEM
22 PRINT_NEWLINE
23 JUMP_FORWARD 0 (to 26)
>> 26 LOAD_CONST 0 (None)
29 RETURN_VALUE
<module 'test' from 'test.py'>
python> = 2.6,
se escrevermos como
if A:
irá gerar um aviso como,
FutureWarning: O comportamento deste método será alterado em versões futuras. Use o teste específico 'len (elem)' ou 'elem is not None'.
Para que possamos usar
if A is not None:
A is not None
é mais rápido, pois há muito menos trabalho a ser feito #