Saltar al contenido

Uso de True, False y None como valores de retorno en funciones de Python

Recabamos por diferentes foros para así tener para ti la solución para tu problema, si tienes alguna pregunta déjanos tu comentario y respondemos sin falta.

Solución:

El consejo no es que nunca debas usarTrue, 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 true. Entonces cuando escribes if x == True Python va a evaluar primero x == True, que se convertirá en True si x era True y False de lo contrario, y luego proceda si el resultado de eso es true. Pero si estas esperando x ser cualquiera True o False, ¿por qué no usar if x ¡directamente!

Igualmente, x == False generalmente puede ser reemplazado por not x.

Hay algunas circunstancias en las que es posible que desee utilizar x == True. Esto es porque un if la condición de la declaración se “evalúa en contexto booleano” para ver si es “veraz” en lugar de probarla exactamente contra True. Por ejemplo, las cadenas, listas y diccionarios no vacíos se consideran veraces por una declaración if, así como valores numéricos distintos de cero, pero ninguno de ellos es igual a True. Entonces, si desea probar si un valor arbitrario es exactamente el valor True, no solo si es veraz, cuándo usarías if x == True. Pero casi nunca veo un uso para eso. Es tan raro que si tu hacer alguna vez necesite escribir eso, vale la pena agregar un comentario para que los futuros desarrolladores (incluido posiblemente usted mismo) no solo asuman el == True es superfluo y eliminarlo.


Utilizando x is True en cambio, es peor. Nunca deberías usar is con tipos inmutables integrados básicos como booleanos (True, False), números y cadenas. La razón es que para estos tipos nos preocupamos valores, no identidad. == prueba que los valores son los mismos para estos tipos, mientras que is siempre prueba las identidades.

Probar identidades en lugar de valores es malo porque, en teoría, una implementación podría construir nuevos valores booleanos en lugar de buscar los existentes, lo que lleva a tener dos True valores que tienen el mismo valor, pero que se almacenan en diferentes lugares de la memoria y tienen diferentes identidades. En la práctica estoy bastante seguro 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 molesta a la gente todo el tiempo con cadenas, porque Python recicla las cadenas cortas y las cadenas literales que aparecen directamente en la fuente del programa. 'foo' is 'foo' siempre regresa True. Pero es fácil construir lo mismo string 2 formas diferentes y haga que Python les dé diferentes identidades. Observe lo siguiente:

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

EDITAR: Entonces, resulta que la igualdad de Python en los booleanos es un poco inesperada (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 de esto, como se explica en las notas cuando se introdujeron bools en Python 2.3.5, es que el antiguo comportamiento de usar los enteros 1 y 0 para representar Verdadero y Falso era bueno, pero solo queríamos nombres más descriptivos para los números que pretendíamos para representar valores de verdad.

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

Entonces está mal usar x is True cuando x podría haber sido establecido por algún código que espera que “Verdadero es solo otra forma de deletrear 1”, porque hay muchas formas de construir valores que son iguales a True pero no tienen la misma identidad que él:

>>> 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 está mal usar x == True cuando x podría ser un valor de Python arbitrario y solo desea saber si es el valor booleano True. La única certeza que tenemos es que con solo usar x es mejor cuando solo desea probar la “veracidad”. Afortunadamente, eso suele ser todo lo que se requiere, ¡al menos en el código que escribo!

Una forma más segura sería x == True and type(x) is bool. Pero eso se está volviendo bastante detallado para un caso bastante oscuro. Tampoco se ve muy Pythonic al hacer una verificación de tipo explícita … pero eso es realmente lo que estás haciendo cuando intentas probar con precisión True en lugar de veracidad; la forma de escribir pato sería aceptar valores veraces y permitir que cualquier clase definida por el usuario se declare veraz.

Si está lidiando con esta noción de verdad extremadamente precisa en la que no solo no considera que las colecciones no vacías sean true pero tampoco consideres que 1 sea true, luego solo usa x is True probablemente esté bien, porque presumiblemente entonces sabes que x no provino de un código que considera que 1 es true. No creo que haya ninguna forma de pitón pura para pensar en otra True que vive en una dirección de memoria diferente (aunque probablemente podría hacerlo desde C), por lo que esto nunca debería romperse a pesar de ser teóricamente lo “incorrecto”.

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

Finalizar edición


En el caso de None, sin embargo, el modismo es usar if x is None. En muchas circunstancias puede utilizar if not x, porque None es un valor “falso” para un if declaración. Pero es mejor hacer esto solo si desea tratar todos los valores falsey (tipos numéricos de valor cero, colecciones vacías y None) de la misma manera. Si está tratando con un valor que es 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 usar if x is None para que no asuma accidentalmente que la función falló cuando simplemente devolvió una lista vacía, o el número 0.

Mis argumentos para usar == en vez de is para tipos de valores inmutables sugeriría que debería usar if x == None en vez de if x is None. Sin embargo, en el caso de None Python garantiza explícitamente que hay exactamente uno None en todo el universo, y el código Python idiomático normal utiliza is.


Respecto a si regresar None o plantear una excepción, depende del contexto.

Por algo como tu get_attr ejemplo, esperaría que genere una excepción, porque lo voy a llamar como do_something_with(get_attr(file)). La expectativa normal de las personas que llaman es que obtendrán la attribute valor, y hacer que obtengan None y asumir que era el attribute El valor es un peligro mucho peor que olvidarse de manejar la excepción cuando realmente puede continuar si el attribute no se puede encontrar. Además, regresando None indicar falla significa que None no es un valor válido para el attribute. Esto puede ser un problema en algunos casos.

Para una función imaginaria como see_if_matching_file_exists, al que proporcionamos un patrón y verifica varios lugares para ver si hay una coincidencia, podría devolver una coincidencia si encuentra una o None si no es así. 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 recupero algo).

Entonces, al elegir entre excepciones y None para indicar falla, debe decidir si None es un valor esperado sin fallas, y luego observe las expectativas del código que llama a la función. Si la expectativa “normal” es que habrá un valor válido devuelto, y sólo ocasionalmente una persona que llama podrá trabajar bien independientemente de que se devuelva un valor válido o no, entonces debe usar excepciones para indicar falla. Si será bastante común que no haya un valor válido, por lo que las personas que llaman esperarán manejar ambas posibilidades, entonces puede usar None.

Usar if foo o if not foo. No hay necesidad de ninguno == o is para eso.

Para verificar con Ninguno, is None y is not None se recomiendan. Esto le permite distinguirlo de Falso (o cosas que se evalúan como Falso, como "" y []).

Si get_attr debería volver None Dependería del contexto. Es posible que tenga un attribute donde el valor es Ninguno, y no podría hacer eso. Yo interpretaría None en el sentido de “no armado”, y un KeyError significaría el key no existe en el archivo.

Si busca la verdad:

if foo

Para false:

if not foo

Para ninguno:

if foo is None

Para los que no son ninguno:

if foo is not None

Para getattr() el comportamiento correcto es no regresar None, pero levanta un AttributError error en su lugar, a menos que su clase sea algo como defaultdict.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *