Por que "if not someobj:" é melhor que "if someobj == None:" no Python?


127

Eu já vi vários exemplos de código como este:

if not someobj:
    #do something

Mas eu estou querendo saber por que não fazer:

if someobj == None:
    #do something

Existe alguma diferença? Um tem uma vantagem sobre o outro?


13
Geralmente, 'someobj is None' é preferível a 'someobj == None' #
Aaron Maenpaa 20/09/08

e o pseudocódigo se X não for Nenhum? Ou X != None?
Charlie Parker

esta pergunta é mais detalhada do que as outras ... parabéns ...
alamin

Respostas:


187

No primeiro teste, o Python tenta converter o objeto em um boolvalor, caso ainda não o seja. A grosso modo, estamos perguntando o objeto: você é significativo ou não? Isso é feito usando o seguinte algoritmo:

  1. Se o objeto tiver um __nonzero__método especial (assim como os embutidos numéricos inte float), ele chamará esse método. Ele deve retornar um boolvalor que é então usado diretamente ou um intvalor que é considerado Falseigual a zero.

  2. Caso contrário, se o objeto tem um __len__método especial (como fazem recipiente built-ins, list, dict, set, tuple, ...), ele chama esse método, considerando um recipiente Falsese ela estiver vazia (comprimento é zero).

  3. Caso contrário, o objeto será considerado, a Truemenos que None, nesse caso, seja considerado False.

No segundo teste, o objeto é comparado para igualdade com None. Aqui, estamos perguntando ao objeto: "Você é igual a esse outro valor?" Isso é feito usando o seguinte algoritmo:

  1. Se o objeto tiver um __eq__método, ele será chamado e o valor de retorno será convertido em um boolvalor e usado para determinar o resultado de if.

  2. Caso contrário, se o objeto tiver um __cmp__método, ele será chamado. Esta função deve retornar um intindicando a ordem dos dois objetos ( -1se self < other, 0se self == other, +1se self > other).

  3. Caso contrário, o objeto é comparado à identidade (ou seja, é referência ao mesmo objeto, como pode ser testado pelo isoperador).

É possível outro teste usando o isoperador. Nós estaríamos perguntando ao objeto: "Você é esse objeto em particular?"

Geralmente, eu recomendaria usar o primeiro teste com valores não numéricos, usar o teste de igualdade quando desejar comparar objetos da mesma natureza (duas cadeias, dois números, ...) e verificar a identidade apenas quando usando valores sentinela ( Noneou seja, não inicializado para um campo de membro por exemplo, ou ao usar getattros __getitem__métodos ou ).

Para resumir, temos:

>>> class A(object):
...    def __repr__(self):
...        return 'A()'
...    def __nonzero__(self):
...        return False

>>> class B(object):
...    def __repr__(self):
...        return 'B()'
...    def __len__(self):
...        return 0

>>> class C(object):
...    def __repr__(self):
...        return 'C()'
...    def __cmp__(self, other):
...        return 0

>>> class D(object):
...    def __repr__(self):
...        return 'D()'
...    def __eq__(self, other):
...        return True

>>> for obj in ['', (), [], {}, 0, 0., A(), B(), C(), D(), None]:
...     print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \
...         (repr(obj), bool(obj), obj == None, obj is None)
  '': bool(obj) -> False, obj == None -> False, obj is None -> False
  (): bool(obj) -> False, obj == None -> False, obj is None -> False
  []: bool(obj) -> False, obj == None -> False, obj is None -> False
  {}: bool(obj) -> False, obj == None -> False, obj is None -> False
   0: bool(obj) -> False, obj == None -> False, obj is None -> False
 0.0: bool(obj) -> False, obj == None -> False, obj is None -> False
 A(): bool(obj) -> False, obj == None -> False, obj is None -> False
 B(): bool(obj) -> False, obj == None -> False, obj is None -> False
 C(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
 D(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
None: bool(obj) -> False, obj == None ->  True, obj is None ->  True

4
Embora tecnicamente correto, isso não explica que tuplas, listas, dictos, strs, unicodes, ints, floats etc. têm um diferente de zero . É muito mais comum confiar no valor verdadeiro do tipo interno do que confiar em um método diferente de zero personalizado .
Ddaa 19/09/08

50

Na verdade, essas são duas práticas ruins. Era uma vez, considerado OK tratar casualmente Nenhum e Falso como semelhantes. No entanto, desde o Python 2.2, essa não é a melhor política.

Primeiro, quando você faz um if xou if not xtipo de teste, o Python precisa converter implicitamente xem booleano. As regras para a boolfunção descrevem uma série de coisas que são falsas; tudo o resto é verdadeiro. Se o valor de x não era propriamente booleano, essa conversão implícita não é realmente a maneira mais clara de dizer as coisas.

Antes do Python 2.2, não havia função bool, portanto era ainda menos clara.

Segundo, você realmente não deveria testar == None. Você deve usar is Nonee is not None.

Consulte PEP 8, Guia de estilo para código Python .

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

  Also, beware of writing "if x" when you really mean "if x is not None"
  -- e.g. when testing whether a variable or argument that defaults to
  None was set to some other value.  The other value might have a type
  (such as a container) that could be false in a boolean context!

Quantos singletons existem? Cinco: None, True, False, NotImplementede Ellipsis. Já que é improvável que você use NotImplementedou Ellipsisnunca diria if x is True(porque simplesmente if xé muito mais claro), você só testará None.


3
A segunda forma é categoricamente não uma prática ruim. O PEP 8 recomenda o uso de se x duas vezes . Primeiro para sequências (em vez de usar len) e depois para Verdadeiro e Falso (em vez de usar é). Praticamente todo o código Python que eu vi usa se x e se não x.
Antti Rasinen 19/09/08

35

Porque Nonenão é a única coisa que é considerada falsa.

if not False:
    print "False is false."
if not 0:
    print "0 is false."
if not []:
    print "An empty list is false."
if not ():
    print "An empty tuple is false."
if not {}:
    print "An empty dict is false."
if not "":
    print "An empty string is false."

False, 0, (), [], {}E ""são todos diferentes None, para que seus dois trechos de código são não equivalente.

Além disso, considere o seguinte:

>>> False == 0
True
>>> False == ()
False

if object:não é uma verificação de igualdade. 0, (), [], None, {}, Etc. são todos diferentes uns dos outros, mas todos eles avaliar a False.

Esta é a "mágica" por trás de expressões de curto-circuito como:

foo = bar and spam or eggs

que é uma abreviação para:

if bar:
    foo = spam
else:
    foo = eggs

embora você realmente deva escrever:

foo = spam if bar else egg

Em relação à sua última pergunta, eles são equivalentes.
Sylvain Defresne 19/09/08

E ambos incorretos porque "" é falso. O segundo deve ler '("",) ou ("s",)'. De qualquer forma, as versões modernas do python têm um operador ternário adequado. Este hack propenso a erros deve ser banido.
Ddaa 19/09/08

4

O PEP 8 - Guia de Estilo para Código Python recomenda o uso é ou não, se você estiver testando o None-ness

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

Por outro lado, se você estiver testando mais do que Nada, deve usar o operador booleano.


3

Se você perguntar

if not spam:
    print "Sorry. No SPAM."

o método __nonzero__ de spam é chamado. No manual do Python:

__nonzero__ ( self ) Chamado para implementar testes de valor verdade e a operação interna bool (); deve retornar False ou True, ou seus equivalentes inteiros 0 ou 1. Quando esse método não é definido, __len __ () é chamado, se for definido (veja abaixo). Se uma classe não define __len __ () nem __nonzero __ (), todas as suas instâncias são consideradas verdadeiras.

Se você perguntar

if spam == None:
    print "Sorry. No SPAM here either."

o método __eq__ de spam é chamado com o argumento Nenhum .

Para obter mais informações sobre as possibilidades de personalização, consulte a documentação do Python em https://docs.python.org/reference/datamodel.html#basic-customization


2

Essas duas comparações servem a propósitos diferentes. O primeiro verifica o valor booleano de algo, o segundo verifica a identidade com o valor Nenhum.


1
Para estar absolutamente correto, o segundo verifica a igualdade com o valor Nenhum ... "someobj is None" verifica a identidade.
Matthew Trevor

0

Por um lado, o primeiro exemplo é mais curto e parece melhor. De acordo com os outros posts, o que você escolhe também depende do que você realmente deseja fazer com a comparação.


0

A resposta é "depende".

Eu uso o primeiro exemplo se considerar 0, "", [] e False (lista não exaustiva) como equivalentes a Nenhum neste contexto.


0

Pessoalmente, escolhi uma abordagem consistente entre os idiomas: eu faço if (var)(ou equivalente) apenas se var for declarado como booleano (ou definido como tal, em C não temos um tipo específico). Eu prefixo essas variáveis ​​com a b(então seria bVarrealmente) para ter certeza de que não usarei acidentalmente outro tipo aqui.
Eu realmente não gosto de conversão implícita para booleana, menos ainda quando existem inúmeras regras complexas.

Claro, as pessoas vão discordar. Alguns vão além, eu vejo if (bVar == true)no código Java no meu trabalho (redundante demais para o meu gosto!), Outros adoram sintaxe compacta demais, indo while (line = getNextLine())(ambígua demais para mim).

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.