¿Por qué es "if not someobj:" mejor que "if someobj == None:" en Python?

127

He visto varios ejemplos de código como este:

if not someobj:
    #do something

Pero me pregunto por qué no hacer:

if someobj == None:
    #do something

¿Hay alguna diferencia? ¿Uno tiene una ventaja sobre el otro?

fuentesjr
fuente
13
En general, 'someobj is None' es preferible a 'someobj == None'
Aaron Maenpaa
¿Qué pasa con el pseudocódigo si X no es Ninguno? O X != None?
Charlie Parker, el
esta pregunta es más detallada que las otras ... felicitaciones ...
alamin

Respuestas:

187

En la primera prueba, Python intenta convertir el objeto a un boolvalor si aún no lo es. Aproximadamente, le preguntamos al objeto: ¿tiene sentido o no? Esto se hace usando el siguiente algoritmo:

  1. Si el objeto tiene un __nonzero__método especial (como los incorporados numéricos, inty float), llama a este método. Debe devolver un boolvalor que luego se usa directamente, o un intvalor que se considera Falsesi es igual a cero.

  2. De lo contrario, si el objeto tiene un __len__método especial (al igual que los contenedores empotrados, list, dict, set, tuple, ...), se llama a este método, teniendo en cuenta un contenedor Falsesi está vacío (longitud es cero).

  3. De lo contrario, se considera el objeto a Truemenos que sea Noneen cuyo caso, se considera False.

En la segunda prueba, el objeto se compara por igualdad con None. Aquí, le preguntamos al objeto: "¿Eres igual a este otro valor?" Esto se hace usando el siguiente algoritmo:

  1. Si el objeto tiene un __eq__método, se llama y el valor de retorno se convierte en un boolvalor y se utiliza para determinar el resultado de if.

  2. De lo contrario, si el objeto tiene un __cmp__método, se llama. Esta función debe devolver una intindicación del orden de los dos objetos ( -1if self < other, 0if self == other, +1if self > other).

  3. De lo contrario, los objetos se comparan por identidad (es decir, son referencias al mismo objeto, como puede ser probado por el isoperador).

Hay otra prueba posible usando el isoperador. Le estaríamos preguntando al objeto: "¿Eres este objeto en particular?"

En general, recomendaría usar la primera prueba con valores no numéricos, para usar la prueba de igualdad cuando desee comparar objetos de la misma naturaleza (dos cadenas, dos números, ...) y verificar la identidad solo cuando usando valores centinela (es Nonedecir, no inicializado para un campo miembro por ejemplo, o cuando se usan getattrlos __getitem__métodos o ).

Para resumir, tenemos:

>>> 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
Sylvain Defresne
fuente
44
Aunque técnicamente correcto, esto no explica que las tuplas, listas, dictos, strs, unicodes, ints, flotantes, etc. tengan un valor distinto de cero . Es mucho más común confiar en el valor de verdad del tipo incorporado que confiar en un método personalizado distinto de cero .
ddaa
50

Estas son en realidad ambas malas prácticas. Érase una vez, se consideró correcto tratar casualmente None y False como similares. Sin embargo, desde Python 2.2 esta no es la mejor política.

En primer lugar, cuando se hace una if xo if not xtipo de prueba, Python tiene para convertir implícitamente xa booleano. Las reglas para la boolfunción describen una serie de cosas que son falsas; todo lo demás es verdad. Si el valor de x no era adecuadamente booleano para empezar, esta conversión implícita no es realmente la forma más clara de decir las cosas.

Antes de Python 2.2, no había una función bool, por lo que era aún menos clara.

En segundo lugar, no deberías probar con == None. Deberías usar is Noney is not None.

Consulte PEP 8, Guía de estilo para el 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!

¿Cuántos singletons hay? Cinco: None, True, False, NotImplementedy Ellipsis. Dado que es muy poco probable que use NotImplementedo Ellipsis, y nunca diría if x is True(porque simplemente if xes mucho más claro), solo lo probará None.

S.Lott
fuente
3
La segunda forma categóricamente no es una mala práctica. PEP 8 recomienda usar if x dos veces . Primero para secuencias (en lugar de usar len) y luego para Verdadero y Falso (en lugar de usar is). Prácticamente todo el código Python que he visto usa if x y si no x.
Antti Rasinen
35

Porque Noneno es lo único que se considera falso.

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, (), [], {}Y ""son todas diferentes None, por lo que sus dos fragmentos de código son no equivalentes.

Además, considere lo siguiente:

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

if object:No es un control de igualdad. 0, (), [], None, {}, Etc son todas diferentes entre sí, pero todos ellos evalúan como False.

Esta es la "magia" detrás de expresiones de cortocircuito como:

foo = bar and spam or eggs

que es la abreviatura de:

if bar:
    foo = spam
else:
    foo = eggs

aunque realmente deberías escribir:

foo = spam if bar else egg
badp
fuente
En cuanto a su última pregunta, son equivalentes.
Sylvain Defresne
Y ambos incorrectos porque "" es falso. El segundo debería leer '("",) o ("s",)'. De todos modos, las versiones modernas de python tienen un operador ternario adecuado. Este truco propenso a errores debe ser desterrado.
ddaa
4

PEP 8: la guía de estilo para el código Python recomienda usar es o no si está probando la ausencia de ninguno

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

Por otro lado, si está probando más de None-ness, debe utilizar el operador booleano

Vinko Vrsalovic
fuente
3

Si preguntas

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

Se llama al método __nonzero__ de spam . Del manual de Python:

__nonzero__ ( self ) Llamado para implementar pruebas de valor de verdad y la operación incorporada bool (); debe devolver False o True, o sus equivalentes enteros 0 o 1. Cuando este método no está definido, se llama a __len __ (), si está definido (ver más abajo). Si una clase no define __len __ () ni __nonzero __ (), todas sus instancias se consideran verdaderas.

Si preguntas

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

la __eq__ método de correo no deseado es llamado con el argumento Ninguno .

Para obtener más información sobre las posibilidades de personalización, consulte la documentación de Python en https://docs.python.org/reference/datamodel.html#basic-customization

Pi.
fuente
2

Estas dos comparaciones tienen diferentes propósitos. El primero verifica el valor booleano de algo, el segundo verifica la identidad con el valor Ninguno.

zgoda
fuente
1
Para ser absolutamente correcto, el segundo verifica la igualdad con un valor None ... "someobj is None" verifica la identidad.
Matthew Trevor
0

Por un lado, el primer ejemplo es más corto y se ve mejor. Según las otras publicaciones, lo que elijas también depende de lo que realmente quieras hacer con la comparación.

rslite
fuente
0

La respuesta es, depende".

Utilizo el primer ejemplo si considero que 0, "", [] y False (la lista no es exhaustiva) son equivalentes a None en este contexto.

Matthias Kestenholz
fuente
0

Personalmente, elegí un enfoque coherente en todos los idiomas: lo hago if (var)(o equivalente) solo si var se declara como booleano (o se define como tal, en C no tenemos un tipo específico). Incluso prefijo estas variables con un b(por lo que sería en bVarrealidad) para asegurarme de que no usaré accidentalmente otro tipo aquí.
Realmente no me gusta el casting implícito a booleano, mucho menos cuando hay numerosas reglas complejas.

Por supuesto, la gente no estará de acuerdo. Algunos van más allá, veo if (bVar == true)en el código de Java en mi trabajo (¡demasiado redundante para mi gusto!), A otros les encanta la sintaxis compacta, ir while (line = getNextLine())(demasiado ambiguo para mí).

PhiLho
fuente