not - python// operator example




Uso extraño del operador "y"/"o" (6)

Estoy tratando de aprender Python y encontré un código que es agradable y corto pero que no tiene ningún sentido

el contexto fue:

def fn(*args):
    return len(args) and max(args)-min(args)

Entiendo lo que está haciendo, pero ¿por qué Python hace esto, es decir, devuelve el valor en lugar de Verdadero / Falso?

10 and 7-2

devuelve 5. Del mismo modo, cambiar el y hacia o dará como resultado un cambio en la funcionalidad. Asi que

10 or 7 - 2

Volvería 10.

¿Es este estilo legítimo / confiable, o hay alguna trampa en esto?


¿Es este estilo legítimo / confiable, o hay alguna trampa en esto?

Esto es legítimo, es una evaluación de cortocircuito donde se devuelve el último valor.

Tu das un buen ejemplo. La función devolverá 0 si no se pasan argumentos, y el código no tiene que verificar un caso especial de no pasar argumentos.

Otra forma de usar esto es predeterminar los argumentos None a una primitiva mutable, como una lista vacía:

def fn(alist=None):
    alist = alist or []
    ....

Si se pasa algún valor no verdadero a la alist se establece de manera predeterminada en una lista vacía, una forma práctica de evitar una declaración if y el error de argumento por defecto mutable


¿Es este estilo legítimo / confiable, o hay alguna trampa en esto?

Me gustaría agregar a esta pregunta que no solo es legítimo y confiable, sino también ultra práctico. Aquí hay un ejemplo simple:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

Por lo tanto, realmente puede usarlo a su favor. Para ser conciso, así es como lo veo:

Or operador

Python or operador devuelve el primer valor Verdad-y, o el último valor, y se detiene

And operador

Python and operador devuelven el primer valor False-y, o el último valor, y se detiene

Entre bastidores

En python, todos los números se interpretan como True excepto 0. Por lo tanto, dice:

0 and 10 

es lo mismo que:

False and True

Lo cual es claramente False . Por lo tanto, es lógico que devuelva 0


TL; DR

Comenzamos resumiendo los dos comportamientos de los dos operadores lógicos and y or . Estos modismos formarán la base de nuestra discusión a continuación.

and

Devuelve el primer valor de Falsy si hay alguno; de lo contrario, devuelve el último valor de la expresión.

or

Devuelve el primer valor de Verdad si hay alguno; de lo contrario, devuelve el último valor de la expresión.

El comportamiento también se resume en los documentos , especialmente en esta tabla:

El único operador que devuelve un valor booleano independientemente de sus operandos es el operador not .

Evaluaciones de "Verdad" y "Verdad"

La declaración

len(args) and max(args) - min(args)

Es una manera concisa muy pitónica (y posiblemente menos legible) de decir "si args no está vacío, devuelve el resultado de max(args) - min(args) ", de lo contrario devuelve 0 . En general, es una representación más concisa de una expresión if-else . Por ejemplo,

exp1 and exp2

Debería (aproximadamente) traducir a:

r1 = exp1
if not r1:
    r1 = exp2

O equivalente,

r1 = exp1 if exp1 else exp2

Donde exp1 y exp2 son objetos arbitrarios de python, o expresiones que devuelven algún objeto. La clave para comprender los usos de los operadores lógicos and / or aquí es comprender que no están restringidos a operar o devolver valores booleanos. Aquí se puede probar cualquier objeto con un valor de veracidad. Esto incluye int , str , list , dict , tuple , set , NoneType y objetos definidos por el usuario. Las reglas de cortocircuito también se aplican también.

Pero, ¿qué es la verdad?
Se refiere a cómo se evalúan los objetos cuando se usan en expresiones condicionales. @Patrick Haugh resume muy bien la veracidad en esta publicación .

Todos los valores se consideran "verdaderos", excepto los siguientes, que son "falsos":

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] - una list vacía
  • {} - un dict vacío
  • () - una tuple vacía
  • '' - un str vacío
  • b'' - un bytes vacío
  • set() - un set vacío
  • un range vacío, como el range(0)
  • objetos para los cuales
    • obj.__bool__() devuelve False
    • obj.__len__() devuelve 0

Un valor "verdadero" satisfará la verificación realizada por las declaraciones if o while . Usamos "verdad" y "falsedad" para diferenciarnos de los valores bool True y False .

Cómo and funciona

Nos basamos en la pregunta de OP como una continuación de una discusión sobre cómo estos operadores en estos casos.

Dada una función con la definición

def foo(*args):
    ...

¿Cómo devuelvo la diferencia entre el valor mínimo y máximo en una lista de cero o más argumentos?

Encontrar el mínimo y el máximo es fácil (¡use las funciones incorporadas!). El único inconveniente aquí es manejar adecuadamente el caso de la esquina donde la lista de argumentos podría estar vacía (por ejemplo, llamando a foo() ). Podemos hacer ambas cosas en una sola línea gracias al operador and :

def foo(*args):
     return len(args) and max(args) - min(args)

foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

Como se usa y, la segunda expresión también debe evaluarse si la primera es True . Tenga en cuenta que, si la primera expresión se evalúa como verdadera, el valor de retorno es siempre el resultado de la segunda expresión . Si la primera expresión se evalúa como Falsy, el resultado devuelto es el resultado de la primera expresión.

En la función anterior, si foo recibe uno o más argumentos, len(args) es mayor que 0 (un número positivo), por lo que el resultado devuelto es max(args) - min(args) . OTOH, si no se pasan argumentos, len(args) es 0 que es Falsy, y se devuelve 0 .

Tenga en cuenta que una forma alternativa de escribir esta función sería:

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

O, más concisamente,

def foo(*args):
    return 0 if not args else max(args) - min(args)

Por supuesto, ninguna de estas funciones realiza ninguna verificación de tipo, por lo que a menos que confíe completamente en la entrada proporcionada, no confíe en la simplicidad de estas construcciones.

Cómo or funciona

Explico el funcionamiento de una manera similar o con un ejemplo artificial.

Dada una función con la definición

def foo(*args):
    ...

¿Cómo completarías foo para devolver todos los números superiores a 9000 ?

Usamos or para manejar el caso de la esquina aquí. Definimos foo como:

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

foo realiza una filtración en la lista para retener todos los números superiores a 9000 . Si existen tales números, el resultado de la comprensión de la lista es una lista no vacía que es Verdad, por lo que se devuelve (cortocircuito en acción aquí). Si no existen tales números, entonces el resultado de la compilación de la lista es [] que es Falsy. Entonces, la segunda expresión ahora se evalúa (una cadena no vacía) y se devuelve.

Usando condicionales, podríamos reescribir esta función como,

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 

    return r

Como antes, esta estructura es más flexible en términos de manejo de errores.


Citando de Python Docs

Tenga en cuenta que ni and ni or restringen el valor y el tipo que devuelven a False y True , sino que devuelven el último argumento evaluado . Esto a veces es útil, por ejemplo, si s es una cadena que debe reemplazarse por un valor predeterminado si está vacía, la expresión s or 'foo' produce el valor deseado.

Entonces, así es como Python fue diseñado para evaluar las expresiones booleanas y la documentación anterior nos da una idea de por qué lo hicieron así.

Para obtener un valor booleano simplemente escríbalo.

return bool(len(args) and max(args)-min(args))

¿Por qué?

Cortocircuito.

Por ejemplo:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

Lo mismo ocurre or también, es decir, devolverá la expresión que es Verdad tan pronto como la encuentre, porque evaluar el resto de la expresión es redundante.

En lugar de devolver True o False True , Python devuelve Truthy o Falsey , que de todos modos se evaluarán como True o False . Podrías usar la expresión como está, y seguirá funcionando.

Para saber qué es Truthy y Falsey , verifique la respuesta de Patrick Haugh


entender de manera simple,

Y: if first_val is False return first_val else second_value

p.ej:

1 and 2 # here it will return 2 because 1 is not False

pero,

0 and 2 # will return 0 because first value is 0 i.e False

y => si alguien es falso, será falso. si ambas son verdaderas, entonces solo se volverá verdad

O: if first_val is False return second_val else first_value

La razón es que si primero es falso, verifique si 2 es verdadero o no.

p.ej:

1 or 2 # here it will return 1 because 1 is not False

pero,

0 or 2 # will return 2 because first value is 0 i.e False

o => si alguien es falso, será cierto. así que si el primer valor es falso, no importa cuál sea el valor 2. entonces devuelve el segundo valor, lo que sea que sea.

Si alguien es verdadero, se volverá verdad. si ambos son falsos, se convertirá en falso.


y / o realizan una lógica booleana, pero devuelven uno de los valores reales cuando se comparan. Al usar y , los valores se evalúan en un contexto booleano de izquierda a derecha. 0, '', [], (), {} y None son falsos en un contexto booleano; Todo lo demás es verdad.

Si todos los valores son verdaderos en un contexto booleano, y devuelve el último valor.

>>> 2 and 5
5
>>> 2 and 5 and 10
10

Si algún valor es falso en un contexto booleano y devuelve el primer valor falso.

>>> '' and 5
''
>>> 2 and 0 and 5
0

Entonces el código

return len(args) and max(args)-min(args)

devuelve el valor de max(args)-min(args) cuando hay args, de lo contrario, devuelve len(args) que es 0.







short-circuiting