2008-09-19 19 views
98

he visto varios ejemplos de código como este:¿Por qué es "if not someobj:" better than "if someobj == None:" en Python?

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?

+13

general 'someObj es None' es preferible 'someObj == None' –

+0

¿qué pasa con pseudocódigo si X no es ninguno? O 'X! = Ninguno'? –

+0

esta pregunta es más detallado que los otros ... felicitaciones ... – alamin

Respuesta

157

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

  1. Si el objeto tiene un método especial __nonzero__ (como lo hacen numéricos muebles empotrados, int y float), se 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__ (como hacen contenedores empotrados, list, dict, set, tuple, ...), se llama a este método, teniendo en cuenta un recipiente False si está vacío (la longitud es cero).

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

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

  1. Si el objeto tiene un método __eq__, se llama, y ​​el valor de retorno se convierte después en un valor bool y se utiliza para determinar el resultado de la if.

  2. De lo contrario, si el objeto tiene un método __cmp__, se llama. Esta función debe devolver un int que indica 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 para la identidad (es decir, son referencia del mismo objeto, como puede ser probado por el operador is).

Hay otra prueba posible con el operador is. Le estaríamos preguntando al objeto, "¿Es usted este objeto en particular?"

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

En resumen, 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 
+4

Aunque técnicamente correcto, esto no explica que las tuplas, listas, predice, RTS, unicodes, enteros, flotadores, etc. tienen una __nonzero__. Es mucho más común que confiar en el valor de verdad de una función de tipo de confiar en un método __nonzero__ personalizado. – ddaa

32

Debido None no es la única cosa que se considera 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, (), [], {} y "" son todos diferentes de None, por lo que sus dos fragmentos de código son no equivalente.

Además, tenga en cuenta lo siguiente:

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

if object: es no una comprobación de igualdad. 0, (), [], None, {}, etc. son todas diferentes entre sí, pero todos ellos evaluar en Falso.

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

foo = bar and spam or eggs 

que es la abreviatura de:

if bar: 
    foo = spam 
else: 
    foo = eggs 

aunque realmente debería escribir:

foo = spam if bar else egg 
+0

En cuanto a su última pregunta, son equivalentes. –

+0

Y ambos incorrecta porque "" es falso. El segundo debe leer '("",) o ("s",)'. De todos modos, las versiones modernas de pitón tienen un operador ternario adecuado. Este truco propenso a errores debería ser desterrado. – ddaa

+0

Gracias. Se eliminó la parte incorrecta. – badp

2

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

+1

Para ser absolutamente correcto, los controles de segundo para -equality- con un valor Ninguno ... "someObj hay ninguno" controles de identidad. –

0

Para uno, 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.

3

PEP 8 -- Style Guide for Python Code recomienda utilizar es o ¿no si está probando para Ninguno-dad

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

Por otro lado, si está tratando de detectar más de Ninguna, debe usar el operador booleano.

0

La respuesta es "depende".

Utilizo el primer ejemplo si considero 0, "", [] y False (lista no exhaustiva) para que sea equivalente a Ninguno en este contexto.

0

Personalmente, elegí un enfoque coherente en todos los idiomas: 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 bVar en realidad) para asegurarme de que no usaré accidentalmente otro tipo aquí.
Realmente no me gusta la conversión implícita a boolean, incluso menos cuando existen 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!), Otros adoran demasiada sintaxis compacta, yendo a while (line = getNextLine()) (demasiado ambigua para mí).

42

Estas son en realidad prácticas deficientes. Hubo un tiempo en que se consideraba aceptable tratar casualmente Ninguno y Falso como similares. Sin embargo, desde Python 2.2 esta no es la mejor política.

En primer lugar, cuando realiza un tipo de prueba if x o if not x, Python tiene que convertir implícitamente x en booleano. Las reglas para la función bool describen una gran cantidad de cosas que son falsas; todo lo demás es Verdadero Si el valor de x no era propiamente booleano para empezar, esta conversión implícita no es realmente la forma más clara de decir cosas.

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

En segundo lugar, realmente no debería probar con == None. Debe usar is None y is not None.

Ver PEP 8, Style Guide for Python Code.

- 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 embarazos únicos hay? Cinco: None, True, False, NotImplemented y Ellipsis. Dado que es poco probable que use NotImplemented o Ellipsis, y nunca dirá if x is True (porque simplemente if x es mucho más claro), solo podrá probar None.

+3

La segunda forma no es una mala práctica. PEP 8 recomienda usar si x _twice_. Primero para las secuencias (en lugar de usar len) y luego para True y False (en lugar de usar is). Prácticamente todo el código Python que he visto utiliza si x y si no es x. –

3

Si preguntas

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

el método __nonzero__ de correo no deseado es llamado. Del manual de Python:

__nonzero__ (auto) Llamado para implementar las pruebas de valor de verdad, y la operación bool incorporado(); debería devolver False o True, o sus enteros equivalentes 0 o 1. Cuando este método no está definido, se llama a __len __(), si está definido (ver a continuación). Si una clase no 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 correo no deseado es llamado con el argumento Ninguno.

Para obtener más información sobre las posibilidades de personalización de echar un vistazo a la Documenation Python en https://docs.python.org/reference/datamodel.html#basic-customization

Cuestiones relacionadas