2012-02-29 18 views
37

Creo que entiendo esto completamente, pero solo quiero asegurarme ya que sigo viendo a la gente decir que NUNCA SIEMPRE prueba contra True, False o None. Sugieren que las rutinas deberían generar un error en lugar de devolver False o None. De todos modos, tengo muchas situaciones en las que simplemente quiero saber si una bandera está configurada o no, por lo que mi función devuelve True o False. Hay otras situaciones en las que tengo una función que devuelve None si no hubo ningún resultado útil. Desde mi pensamiento, ni es problemático, siempre y cuando me doy cuenta que nunca se debe utilizar:Uso de verdadero, falso y ninguno como valores devueltos en las funciones de python

if foo == True 
if foo == False 
if foo == None 

y en su lugar debería utilizar:

if foo is True 
if foo is False 
if foo is None 

desde Verdadero, Falso, y ninguno es todos los embarazos únicos y siempre evaluar la forma en que espero al usar "es" en lugar de "==". ¿Me equivoco aquí?

En la misma línea, ¿sería más pitónico modificar las funciones que algunas veces devuelven None para que aparezcan un error? Supongamos que tengo un método de instancia llamado "get_attr()" que recupera un atributo de algún archivo. En el caso donde encuentra que el atributo que solicité no existe, ¿es apropiado devolver None? ¿Sería mejor hacerles plantear un error y atraparlo más tarde?

Respuesta

62

El consejo no es que nunca se debe usoTrue, False o None. Es solo que no deberías usar if x == True.

if x == True es tonto porque == es solo un operador binario! Tiene un valor de retorno de True o False, dependiendo de si sus argumentos son iguales o no. Y if condition procederá si condition es verdadero. Por lo tanto, cuando escriba if x == True Python va a evaluar primero x == True, que se convertirá en si x fue y False en caso contrario, y luego proceder si el resultado es cierto. Pero si espera que x sea True o False, ¿por qué no simplemente usa if x directamente?

Del mismo modo, x == False generalmente puede ser reemplazado por not x.

Hay algunas circunstancias en las que es posible que desee utilizar x == True. Esto se debe a que una condición de enunciado if se "evalúa en contexto booleano" para ver si es "veraz" en lugar de probar exactamente en contra de True. Por ejemplo, las cadenas, listas y diccionarios no vacíos son considerados como verdad por una instrucción if, así como por valores numéricos distintos de cero, pero ninguno de ellos es igual a True. Por lo tanto, si desea probar si un valor arbitrario es exactamente el valor True, no solo si es cierto, cuándo usaría if x == True. Pero casi nunca veo un uso para eso. Es tan raro que si hace alguna vez necesita escribir eso, vale la pena agregar un comentario para que los futuros desarrolladores (incluido posiblemente usted mismo) no asuman simplemente que el == True es superfluo y lo elimine.


En su lugar, el uso de x is True es peor. Nunca debe usar is con tipos inmutables incorporados básicos como booleanos (True, False), números y cadenas. La razón es que para estos tipos nos importan valores, no identidad. == prueba que los valores son los mismos para estos tipos, mientras que is siempre prueba identidades.

Probando identidades en lugar de valores es malo porque una implementación podría teóricamente construir nuevos valores booleanos en lugar de buscar los existentes, lo que lleva a tener dos valores True que tienen el mismo valor pero se almacenan en diferentes lugares en la memoria y tienen diferentes identidades En la práctica, estoy bastante seguro de que True y False siempre son reutilizados por el intérprete de Python por lo que esto no sucederá, pero eso es realmente un detalle de implementación. Este problema desplaza a la gente todo el tiempo con cadenas, ya que las cadenas cortas y cadenas literales que aparecen directamente en el origen del programa son recicladas por Python para que 'foo' is 'foo' siempre devuelva True. Pero es fácil construir la misma cadena de 2 formas diferentes y Python les da diferentes identidades. Observe lo siguiente:

>>> stars1 = ''.join('*' for _ in xrange(100)) 
>>> stars2 = '*' * 100 
>>> stars1 is stars2 
False 
>>> stars1 == stars2 
True 

EDIT: Así que resulta que la igualdad de Python en booleanos es un poco inesperado (al menos para mí):

>>> True is 1 
False 
>>> True == 1 
True 
>>> True == 2 
False 
>>> False is 0 
False 
>>> False == 0 
True 
>>> False == 0.0 
True 

La razón para esto, como se explica en the notes when bools were introduced in Python 2.3.5, es que el antiguo comportamiento de usar enteros 1 y 0 para representar True y False era bueno, pero solo queríamos nombres más descriptivos para los números que pretendíamos representar valores de verdad.

Una forma de lograr eso habría sido simplemente tener True = 1 y False = 0 en los builtins; entonces 1 y Verdadero realmente serían indistinguibles (incluso por is). Pero eso también significaría que una función que devuelve True mostraría 1 en el intérprete interactivo, por lo que lo que se ha hecho en su lugar es crear bool como un subtipo de int. Lo único diferente de bool es str y repr; Las instancias bool todavía tienen los mismos datos que las instancias int, y todavía comparan la igualdad de la misma manera, por lo que True == 1.

Por lo que es incorrecto utilizar x is True cuando x podrían haber sido establecido por un código que espera que "la verdadera es más que otra forma de deletrear 1", porque hay un montón de maneras de construir valores que son iguales a True pero no lo hacen tienen la misma identidad que es:

>>> a = 1L 
>>> b = 1L 
>>> c = 1 
>>> d = 1.0 
>>> a == True, b == True, c == True, d == True 
(True, True, True, True) 
>>> a is b, a is c, a is d, c is d 
(False, False, False, False) 

y es incorrecto utilizar x == True cuando x podría suponer un valor arbitrario de Python y sólo desea saber si es el valor booleano True. La única certeza que tenemos es que solo usando x es mejor cuando solo quieres probar la "veracidad". ¡Afortunadamente eso es todo lo que se requiere, al menos en el código que escribo!

Una manera más segura sería x == True and type(x) is bool. Pero eso es bastante detallado para un caso bastante oscuro. Tampoco se ve muy pitónico haciendo una comprobación de tipos explícita ... pero eso es lo que estás haciendo cuando intentas probar con precisión True en lugar de decir la verdad; la manera de tipar pato sería aceptar valores verdaderos y permitir que cualquier clase definida por el usuario se declare veraz.

Si está tratando con esta noción extremadamente precisa de la verdad, en la que no solo considera que las colecciones no vacías son ciertas, sino que también considera que 1 no es verdadero, entonces solo está usando x is True, porque, presumiblemente, entonces usted sabe que x no provino del código que considera que 1 es verdadero. No creo que haya una forma pura de pitón para encontrar otra True que viva en una dirección de memoria diferente (aunque probablemente podrías hacerlo desde C), así que esto nunca debería romperse a pesar de ser teóricamente lo "incorrecto" que hacer.

¡Y solía pensar que los booleanos eran simples!

Fin Editar


En el caso de None, sin embargo, el lenguaje es el uso de if x is None. En muchas circunstancias, puede usar if not x, porque None es un valor "falsey" en una declaración if. Pero lo mejor es hacer esto solo si quiere tratar todos los valores de falsey (tipos numéricos de cero valor, colecciones vacías y None) de la misma manera. Si está tratando con un valor que sea algún otro valor posible o None para indicar "sin valor" (como cuando una función devuelve None en caso de falla), entonces es mucho mejor para usar if x is None para no accidentalmente asumir la función falló cuando acaba de pasar a devolver una lista vacía, o el número 0.

Mis argumentos para utilizar == en lugar de is de los tipos de valores inmutables que sugiere que se debe utilizar en lugar de if x == Noneif x is None. Sin embargo, en el caso de None Python garantiza explícitamente que hay exactamente un None en todo el universo, y el código de Python idiomático normal usa is.


En cuanto a si se debe devolver None o elevar una excepción, que depende del contexto.

Para algo así como su ejemplo get_attr, esperaría que se produjera una excepción, porque la llamaré como do_something_with(get_attr(file)).La expectativa normal de las personas que llaman es que obtendrán el valor del atributo, y que obtengan None y asuman que el valor del atributo es mucho más peligroso que olvidarse de manejar la excepción cuando realmente se puede continuar si el atributo no puede ser encontrado. Además, devolver None para indicar la falla significa que None no es un valor válido para el atributo. Esto puede ser un problema en algunos casos.

Para una función imaginaria como see_if_matching_file_exists, que proporcionamos un patrón y comprueba varios lugares para ver si hay una coincidencia, podría devolver una coincidencia si encuentra uno o None si no lo hace. Pero alternativamente podría devolver una lista de coincidencias; entonces ninguna coincidencia es solo la lista vacía (que también es "falsey"; esta es una de esas situaciones en las que simplemente usaría if x para ver si obtengo algo).

Por lo tanto, al elegir entre excepciones y None para indicar una falla, debe decidir si None es un valor esperado de no falla, y luego observar las expectativas del código que llama a la función. Si la expectativa "normal" es que se devolverá un valor válido, y solo ocasionalmente la persona que llama podrá trabajar bien si se devuelve o no un valor válido, entonces debe usar excepciones para indicar la falla. Si será bastante común que no exista un valor válido, entonces las personas que llaman esperarán manejar ambas posibilidades, luego puede usar None.

+0

Gracias por esta respuesta. Aclaró una cantidad de conceptos erróneos que he tenido durante bastante tiempo y aclaró algunos otros puntos que estaban un poco nublados. Pensé que Verdadero y Falso eran como Ninguno en esa serpiente pitón que garantizaba que sus identificaciones siempre serían las mismas. Lamentablemente, esto significa que tengo un montón de código para ver (grep) a través de ... – Vorticity

+2

@Vorticity: para ser sincero, Python posiblemente * garantice * que sus identificaciones sean siempre las mismas, e incluso si no garantiza que estoy 99% seguro de que siempre serán las mismas en la práctica. Por lo tanto, no necesariamente me apresuro a editar 'x is True' fuera de todo tu código si tienes cosas mejores que hacer (como escribir un nuevo código :)). Pero en general, solo desea utilizar pruebas 'is' con objetos mutables (o' None', que es la excepción que prueba la regla). – Ben

+0

Después de pensar en esto un poco más, tengo una pregunta. ¿Cómo se puede diferenciar entre una variable que contiene 'int (13)' y una que contiene 'True'? ¿Es solo que normalmente no te importa si la variable es realmente "Verdadera" y solo que es verdad? Supongo que estoy preguntando esto porque tengo que analizar de un lado a otro un formato de archivo molesto que contiene '' sí '' y '' no '' por todas partes, pero cuando en mi código prefiero estar tratar con 'True' y' False', así que tuve que escribir código para ir de uno a otro con un mínimo de alboroto. – Vorticity

13

Utilice if foo o if not foo, no hay necesidad de == o is para eso.

Para realizar una comprobación de ninguno, is None y is not None se recomiendan. Esto le permite distinguirlo de False (o cosas que evalúan a False, como "" y []).

Si get_attr debe devolver None dependerá del contexto. Puede tener un atributo donde el valor sea Ninguno y no podrá hacer eso. Interpretaría None como que significa "unset" y un KeyError significaría que la clave no existe en el archivo.

+0

Gracias por la respuesta y por la sugerencia de "if foo". Supongo que siempre lo he evitado por hábito debido a que exactamente lo que usted señala "no es ninguno". En realidad, nunca se me pasó por la mente que "si foo es verdadero" es demasiado largo. – Vorticity

1

Puede verificar directamente que la variable contiene valor o no como if var o not var.

0

En el caso de su función getattr de ficción, si el atributo solicitado siempre debe estar disponible pero no es así, arroje un error. Si el atributo es opcional, devuelve None.

7

Si la comprobación de la verdad:

if foo 

por falso:

if not foo 

para ninguno:

if foo is None 

para no ninguno:

if foo is not None 

Para getattr() el comportamiento correcto es no volver None pero generará un error AttributError vez - a menos que su clase es algo así como defaultdict

6

En cuanto a si se debe elevar una excepción o volver None: depende del caso de uso. Cualquiera de los dos puede ser pitónico. Observe la clase dict de python, por ejemplo, x[y] ganchos en dict.__getitem__ y aumenta KeyError si la clave no está presente. Pero el método dict.get devuelve el segundo argumento (que está predeterminado en None) si la clave no está presente. Ambos son útiles.

Lo más importante que debe tener en cuenta es documentar ese comportamiento en la documentación, y asegúrese de que su método get_attr() hace lo que dice que hace.

Para hacer frente a sus otras preguntas, utilizar estas convenciones:

if foo: 
    # for testing truthiness 

if not foo: 
    # for testing falsiness 

if foo is None: 
    # testing .. Noneliness ? 

if foo is not None: 
    # check explicitly avoids common bugs caused by empty sequences being false 

funciones que devuelven True o False probablemente debería tener un nombre que hace que este evidente de mejorar la legibilidad del código

def is_running_on_windows(): 
    return os.name == 'nt' 

En python3 se puede "tipear-insinuar" que:

>>> def is_running_on_windows() -> bool: 
...  return os.name == 'nt' 
... 
>>> is_running_on_windows.__annotations__ 
{'return': bool} 
0

por cierto, no Ninguno:

if foo: 

por falso, Ninguno:

if not foo: 
1

En los ejemplos de PEP 8(Style Guide for Python Code) document, he visto que se están utilizando foo is None o foo is not None en lugar de foo == None o foo != None. También se recomienda utilizar if boolean_value en este documento en lugar de if boolean_value == True o if boolean_value is True. Así que creo que si esta es la forma oficial de Python, los chicos de Python deberíamos seguir por este camino también.

Atentamente.

0

Una cosa para asegurarse es que nada puede reasignar su variable. Si no es un booleano al final, confiar en la veracidad dará lugar a errores. La belleza de la programación condicional en lenguajes tipados dinámicamente :).

Las siguientes impresiones "no".

x = False 
if x: 
    print 'yes' 
else: 
    print 'no' 

Ahora vamos a cambiar x.

x = 'False' 

Ahora las impresiones de los estados "" debido a que la cadena es Truthy.

if x: 
    print 'yes' 
else: 
    print 'no' 

Esta afirmación, sin embargo, correctamente salidas "sin".

if x == True: 
    print 'yes' 
else: 
    print 'no' 
Cuestiones relacionadas