si A vs si A no es Ninguno:

154

Puedo usar:

if A:

en vez de

if A is not None:

Esto último parece muy detallado. ¿Hay una diferencia?

rbairos
fuente

Respuestas:

149

La declaración

if A:

llamará A.__nonzero__()(consulte la documentación de nombres de métodos especiales ) y utilizará el valor de retorno de esa función. Aquí está el resumen:

object.__nonzero__(self)

Llamado a implementar pruebas de valor de verdad y la operación incorporada bool(); debe devolver Falseo True, o sus equivalentes enteros 0o 1. Cuando este método no está definido, __len__()se llama, si está definido, y el objeto se considera verdadero si su resultado es distinto de cero. Si una clase no define ni __len__()ni __nonzero__(), todas sus instancias se consideran verdaderas.

Por otra parte,

if A is not None:

compara solo la referencia Acon Nonepara ver si es igual o no.

Greg Hewgill
fuente
44
y A is not Nonees más rápido ya que hay mucho menos trabajo por hacer
John La Rooy
40
@gnibbler No según mis pruebas. if object(): passes ~ 0.130 usec por ciclo, mientras que if object() is not None: passes ~ 0.135 usec. De todos modos, no debe usar el rendimiento para elegir entre estos dos, sino observar las diferencias en cómo funcionan, ya que no son equivalentes .
Lauritz V. Thaulow
1
@gnibbler if A is not Noneparece ser más lento porque es una comparación y necesita cargar el singleton incorporado Nonecomo un paso intermedio para comparar A(mira el dis.dis()). Corrígeme si me equivoco pero if A:parece ser más eficiente, tan pronto como realmente quieras probar el valor de verdad y no la Noneidentidad.
cedbeu
2
@cedbeu, parece depender del valor de A. Probé ahora python -m timeit -s"a=0" "if a: pass" "else: pass"es más rápido que python -m timeit -s"a=0" "if a is None: pass" "else: pass"pero python -m timeit -s"a=1" "if a: pass" "else: pass"es más lento. Podría depender de la plataforma, vea si obtiene los mismos resultados
John La Rooy
2
@cedbeu, en Python3 todos son mucho más rápidos, pero la is Noneprueba fue la más lenta para mí. En pypy, todos midieron exactamente lo mismo :)
John La Rooy
51

Como está escrito en PEP8 :

  • Comparaciones con singletons como None siempre se deben hacer con 'is' o 'is not', nunca con los operadores de igualdad .

    Además, tenga cuidado de escribir "if x" cuando realmente quiere decir "if x no es None" , por ejemplo, al probar si una variable o argumento que tiene el valor predeterminado None se estableció en algún otro valor. ¡El otro valor podría tener un tipo (como un contenedor) que podría ser falso en un contexto booleano!

scraplesh
fuente
77
Mmmh, sin embargo, esto no está muy claro y no responde al OP. La redirección automática a la PEP no siempre es la buena respuesta. Muy a menudo uno no está interesado en la prueba de identidad con el singleton None, sino solo en una verificación del valor de verdad. En este caso, if A:parece más eficiente (tome un dis.dis(), hay los pasos adicionales de cargar el builtin Noney comparar if A is not None:, mientras que solo hay un jump_ifen el otro caso).
cedbeu
29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

entonces no es lo mismo que

if x is not None # which works only on None
Abdul Muneer
fuente
17

Muchas funciones devuelven Ninguno si no hay resultados apropiados. Por ejemplo, el .first()método de una consulta SQLAlchemy devuelve None si no había filas en el resultado. Suponga que está seleccionando un valor que podría devolver 0 y necesita saber si en realidad es 0 o si la consulta no tuvo ningún resultado.

Una expresión común es dar a una función o argumento opcional de un método el valor predeterminado de Ninguno, y luego probar que ese valor sea Ninguno para ver si se especificó. Por ejemplo:

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

compara eso con:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

En este último, ¿qué pasa si llamas spam(0)o spam([])? La función detectaría (incorrectamente) que no ha pasado un valor paraeggs y calcularía un valor predeterminado para usted. Eso probablemente no sea lo que quieres.

O imagine un método como "devolver la lista de transacciones para una cuenta determinada". Si la cuenta no existe, podría devolver Ninguno. Esto es diferente a devolver una lista vacía (lo que significaría "esta cuenta existe pero no ha registrado transacciones).

Finalmente, de vuelta a las cosas de la base de datos. Hay una gran diferencia entre NULL y una cadena vacía. Una cadena vacía generalmente dice "hay un valor aquí, y ese valor no es nada". NULL dice "este valor no se ha ingresado".

En cada uno de esos casos, querrás usar if A is None. Está buscando un valor específico, Ninguno, no solo "cualquier valor que se convierta en False".

Kirk Strauser
fuente
10

Hacen cosas muy diferentes .

Las siguientes comprobaciones si A tiene nada, excepto los valores False, [], None, ''y 0. Comprueba el valor de A.

if A:

Lo siguiente verifica si A es un objeto diferente a Ninguno. Comprueba y compara la referencia (dirección de memoria) de A y Ninguna.

if A is not None:

ACTUALIZACIÓN: explicación adicional

Muchas veces los dos parecen hacer lo mismo, por lo que mucha gente los usa indistintamente, es una muy mala idea. La razón por la que los dos dan los mismos resultados es muchas veces por pura coincidencia debido a optimizaciones del intérprete / compilador como pasantías u otra cosa.

Con esas optimizaciones en mente, los enteros y las cadenas del mismo valor terminan usando el mismo espacio de memoria. Eso probablemente explica por qué dos cadenas separadas actúan como si fueran lo mismo.

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

Sin embargo, otras cosas no se comportan igual ...

> a = []
> b = []
> a is b
False
> a == b
True

Las dos listas claramente tienen su propia memoria. Sorprendentemente, las tuplas se comportan como cuerdas.

> a = ()
> b = ()
> a is b
True
> a == b
True

Probablemente esto se deba a que se garantiza que las tuplas no cambiarán, por lo que tiene sentido reutilizar la misma memoria.

La conclusión es que no puedes confiar en las coincidencias . Solo porque grazna como un pato no significa que sea un pato. Use isy ==dependiendo de lo que realmente quiera verificar. Estas cosas pueden ser difíciles de depurar ya que se islee como una prosa que a menudo simplemente pasamos por alto.

Pithikos
fuente
Nonees un singleton, no es un detalle de implementación (a diferencia de int o string interning). No estoy seguro de seguir lo que quieres decir.
Lev Levitsky
@LevLevitsky mi punto no es que se Nonecomporte de manera diferente into strdebido a la internación. Mi punto es eso isy ==estoy revisando cosas diferentes; primero verifica una dirección de memoria, el segundo verifica el contenido de las direcciones de memoria.
Pithikos
@LevLevitsky Edité mi respuesta ahora para que sea un poco más digerible.
Pithikos
7

if A: resultará falso si A es 0, Falso, cadena vacía, lista vacía o Ninguno, lo que puede conducir a resultados no deseados.

keflavich
fuente
1
Y muchos otros valores también, como lista vacía, conjunto vacío, tupla vacía, etc. Esencialmente, cualquier cosa que no sea verdadera por docs.python.org/3/library/stdtypes.html#truth-value-testing .
jarmod
6

La mayoría de las guías que he visto sugieren que debes usar

si A:

a menos que tenga una razón para ser más específico.

Hay algunas pequeñas diferencias. Hay valores distintos de None que devuelven False, por ejemplo, listas vacías, o 0, así que piense en qué es lo que realmente está probando.

Colin Coghill
fuente
5

Ninguno es un valor especial en Python que generalmente designa una variable no inicializada. Para probar si A no tiene este valor particular, utilice:

if A is not None

Los valores de Falsey son una clase especial de objetos en Python (por ejemplo, falso, []). Para probar si A es falsey, use:

if not A

Por lo tanto, las dos expresiones no son lo mismo Y será mejor que no las trates como sinónimos.


PD None también es falsey, por lo que la primera expresión implica la segunda. Pero el segundo cubre otros valores de falsey además de Ninguno. Ahora ... si puede estar seguro de que no puede tener otros valores de falsey además de Ninguno en A, puede reemplazar la primera expresión con la segunda.

mircealungu
fuente
No es que estés equivocado, pero esta respuesta ya cubre esto.
Makoto
boh, me gustaría pedirle que elimine el voto negativo si no le importa. mi respuesta es equivalente a todas las demás, quizás menos con la que se refiere, pero se presenta desde una perspectiva diferente, y podría ser más fácil de entender para alguien. Además, cuando veo un voto negativo en SO, supongo una respuesta incorrecta ... lo cual admite que no es el caso.
mircealungu
Bueno, esa suposición es incompleta. Podría estar equivocado o simplemente no ser útil. Como su respuesta como dije ya estaba cubierta, no estoy convencido de que sea útil.
Makoto
4

Depende del contexto.

Lo uso if A:cuando espero Aser algún tipo de colección, y solo quiero ejecutar el bloque si la colección no está vacía. Esto le permite a la persona que llama pasar cualquier colección con buen comportamiento, vacía o no, y hacer que haga lo que espero. También permite NoneyFalse suprime la ejecución del bloque, que en ocasiones es conveniente para llamar al código.

OTOH, si espero Aser un objeto completamente arbitrario pero podría haber sido predeterminado None, entonces siempre lo uso if A is not None, ya que el código de llamada podría haber pasado deliberadamente una referencia a una colección vacía, una cadena vacía o un tipo numérico con valor 0, o booleanoFalse , o alguna instancia de clase que resulta ser falsa en contexto booleano.

Y por otro lado, si espero Aser algo más específico (por ejemplo, una instancia de una clase a la que llamaré métodos), pero podría haber sido predeterminado None, y considero que la conversión booleana predeterminada es una propiedad de la clase No me importa imponer en todas las subclases, luego usaré if A:para salvar a mis dedos la terrible carga de escribir 12 caracteres adicionales.

Ben
fuente
3

Creé un archivo llamado test.pyy lo ejecuté en el intérprete. Puede cambiar lo que quiera, para probar con seguridad cómo están las cosas detrás de escena.

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

Esta es la diferencia del ensamblador:

Fuente:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>
usuario
fuente
2

El primero es más Pitónico (mejor código ideomático), pero no ejecutará el bloque si A es Falso (no Ninguno).

Borealid
fuente
77
-1. Como @klesh menciona, PEP8 dice usar is/is not None. Siguiendo PEP8 es Pythonic. Además las dos pruebas son diferentes .
JCotton
1

python> = 2.6,

si escribimos como

if A:

generará advertencia como,

Advertencia del futuro: el comportamiento de este método cambiará en futuras versiones. Utilice la prueba específica 'len (elem)' o 'elem is not none' en su lugar.

Entonces podemos usar

if A is not None:
usuario normal
fuente