se A vs se A não for Nenhum:


154

Eu posso usar:

if A:

ao invés de

if A is not None:

O último parece tão detalhado. Existe alguma diferença?

Respostas:


149

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 retornar Falseou True, ou seus equivalentes inteiros 0ou 1. 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 Acom Nonepara ver se ele é o mesmo ou não.


4
e assim A is not Noneé mais rápido, pois há muito menos trabalho a ser feito #
John La Rooy

40
@gnibbler Não está de acordo com meus testes. 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 .
Lauritz V. Thaulow 19/10/11

1
O @gnibbler if A is not Noneparece ser mais lento porque é uma comparação e precisa carregar o singleton interno Nonecomo 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 Noneidentidade.
cedbeu

2
@cedbeu, Parece depender do valor de A. Eu testei agora 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
John La Rooy

2
@cedbeu, em Python3 são todos muito mais rápidos, mas o is Noneteste foi realmente o mais lento para mim. Em PyPy todos eles mediram exatamente o mesmo :)
John La Rooy

51

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!


7
Mmmh, no entanto, isso não está muito claro e não responde ao OP. O redirecionamento automático para o PEP nem sempre é a boa resposta. Muitas vezes, não se interessa pelo teste de identidade com o singleton 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 Nonee comparar, com if A is not None:, enquanto há apenas um jump_ifno outro caso).
cedbeu

29
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

17

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".


10

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 ise ==dependendo do que você realmente deseja verificar. Essas coisas podem ser difíceis de depurar, uma vez que se islê 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.
Lev Levitsky

@LevLevitsky meu argumento não é que Nonese comporte de maneira diferente intou strdevido à internação. Meu ponto é que ise ==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.
Pithikos

@LevLevitsky Editei minha resposta agora para torná-la um pouco mais digerível.
Pithikos


6

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.


5

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.


Não é que você esteja errado, mas esta resposta já cobre isso.
Makoto

boh, eu gostaria de pedir para você remover o voto negativo, se você não se importa. minha resposta é equivalente a todas as outras, talvez menos com a que você se refere, mas é apresentada de uma perspectiva diferente, e pode ser mais fácil de entender para alguém. Além disso, quando vejo um voto negativo no SO, assumo uma resposta errada ... que você admite que não é o caso.
21816 mircealungu

Bem, essa suposição é incompleta. Pode estar errado ou simplesmente não é útil. Como sua resposta, como afirmei, já foi abordada, não estou convencido de que seja útil.
Makoto

4

Depende do contexto.

Uso if A:quando espero Aque 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 NoneeFalse suprime a execução do bloco, o que é ocasionalmente conveniente para chamar código.

OTOH, se eu espero Aque 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 Aser 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.


3

Criei um arquivo chamado test.pye 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'>

2

O primeiro é mais Pythonic (melhor código ideomatic), mas não executará o bloco se A for False (not None).


7
-1. Como o @klesh menciona, o PEP8 diz para usar is/is not None. Após PEP8 é Pythonic. Além dos dois testes são diferentes .
JCotton

1

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:
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.