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?
X != None
?
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?
X != None
?
Respostas:
No primeiro teste, o Python tenta converter o objeto em um bool
valor, caso ainda não o seja. A grosso modo, estamos perguntando o objeto: você é significativo ou não? Isso é feito usando o seguinte algoritmo:
Se o objeto tiver um __nonzero__
método especial (assim como os embutidos numéricos int
e float
), ele chamará esse método. Ele deve retornar um bool
valor que é então usado diretamente ou um int
valor que é considerado False
igual a zero.
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 False
se ela estiver vazia (comprimento é zero).
Caso contrário, o objeto será considerado, a True
menos 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:
Se o objeto tiver um __eq__
método, ele será chamado e o valor de retorno será convertido em um bool
valor e usado para determinar o resultado de if
.
Caso contrário, se o objeto tiver um __cmp__
método, ele será chamado. Esta função deve retornar um int
indicando a ordem dos dois objetos ( -1
se self < other
, 0
se self == other
, +1
se self > other
).
Caso contrário, o objeto é comparado à identidade (ou seja, é referência ao mesmo objeto, como pode ser testado pelo is
operador).
É possível outro teste usando o is
operador. 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 ( None
ou seja, não inicializado para um campo de membro por exemplo, ou ao usar getattr
os __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
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 x
ou if not x
tipo de teste, o Python precisa converter implicitamente x
em booleano. As regras para a bool
funçã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 None
e 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
, NotImplemented
e Ellipsis
. Já que é improvável que você use NotImplemented
ou Ellipsis
nunca diria if x is True
(porque simplesmente if x
é muito mais claro), você só testará None
.
Porque None
nã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
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.
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
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.
A resposta é "depende".
Eu uso o primeiro exemplo se considerar 0, "", [] e False (lista não exaustiva) como equivalentes a Nenhum neste contexto.
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 bVar
realmente) 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).