¿Por qué es “si no someobj:” mejor que “si someobj == None:” en Python?

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?

En la primera prueba, Python intenta convertir el objeto a un valor bool si aún no lo es. A grandes rasgos, estamos preguntando el objeto: ¿eres significativo o no? Esto se hace usando el siguiente algoritmo:

  1. Si el objeto tiene un método especial __nonzero__ (al igual que las incorporaciones numéricas, int y float ), llama a este método. Debe devolver un valor bool que luego se usa directamente o un valor int que se considera False si es igual a cero.

  2. De lo contrario, si el objeto tiene un método especial __len__ (al igual que los contenedores incorporados, list , dict , set , tuple , …), llama a este método, considerando un contenedor False si está vacío (la longitud es cero).

  3. De lo contrario, el objeto se considera True menos que sea None en cuyo caso, se considera False .

En la segunda prueba, el objeto se compara para igualar a 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 método __eq__ , se llama, y ​​el valor de retorno se convierte a un valor bool y se usa para determinar el resultado del if .

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

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

Hay otra prueba posible utilizando el operador is . Estaríamos preguntando el objeto, "¿Eres este objeto en particular?"

En general, recomendaría usar la primera prueba con valores no numéricos, usar la prueba de igualdad cuando quiera comparar objetos de la misma naturaleza (dos cadenas, dos números, ...) y verificar la identidad solo cuando usando valores de centinela ( None significa que no se haya inicializado para un campo miembro por ejemplo, o cuando se usan los getattr o __getitem__ ).

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 

Estas son en realidad las dos malas prácticas. Érase una vez, se consideraba correcto tratar casualmente a Ninguno y Falso como similares. Sin embargo, desde Python 2.2 esta no es la mejor política.

Primero, cuando haces un tipo de prueba if x o if not x , Python tiene que convertir implícitamente x a booleano. Las reglas para la función bool describen una serie de cosas que son falsas; todo lo demás es verdad Si para empezar el valor de x no era correctamente booleano, 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 ninguna función bool, por lo que era aún menos claro.

En segundo lugar, no deberías probar con == None . Usted debe usar is None y is not None .

Ver PEP 8, Guía de estilo para el código de 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" -- eg 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 , no NotImplemented y Ellipsis . Ya que es muy poco probable que uses NotImplemented o Ellipsis , y nunca dirías if x is True (porque simplemente if x es mucho más claro), solo probarás None .

Porque None no 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 diferentes de None , por lo que sus dos fragmentos de código no son equivalentes.

Además, considere lo siguiente:

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

if object: no es un control de igualdad. 0 , () , [] , None , {} , etc. son diferentes entre sí, pero todos se evalúan como Falso.

Esta es la “magia” detrás de las expresiones de cortocircuito como:

 foo = bar and spam or eggs 

que es taquigrafía para:

 if bar: foo = spam else: foo = eggs 

aunque realmente deberías escribir:

 foo = spam if bar else egg 

Si preguntas

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

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

__nonzero__ ( auto ) Llamado para implementar pruebas de valor de verdad, y la operación integrada 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 define ni __len __ () ni __nonzero __ (), todas sus instancias se consideran verdaderas.

Si preguntas

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

El método __eq__ de spam se llama 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

PEP 8: la Guía de estilo para el código de Python que se recomienda utilizar es o no si está probando la no-falta

 - 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 un valor de “No-Ness”, debe usar el operador booleano.

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

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.

La respuesta es, depende”.

Utilizo el primer ejemplo si considero que 0, “”, [] y Falso (la lista no es exhaustiva) es equivalente a Ninguno en este contexto.

Personalmente, elegí un enfoque coherente en todos los idiomas: sí 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 una b (por lo que sería bVar realidad) para estar seguro de que no bVar otro tipo aquí accidentalmente.
Realmente no me gusta la conversión implícita a booleana, y menos aún cuando existen numerosas reglas complejas.

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