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. Entonces

10 or 7 - 2

Volvería 10.

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

92
Marcin 30 oct. 2017 a las 06:16

6 respuestas

¿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 un valor no verdadero a alist, el valor predeterminado es una lista vacía, una forma práctica de evitar una declaración if y la trampa de argumento mutable por defecto

5
salparadise 30 oct. 2017 a las 03:26

¿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 que también es 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 operadora

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

And operadora

El operador and de Python devuelve el primer valor False-y, o el último valor, y se detiene

Detrás de escena

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

0 and 10 

Es la misma que:

False and True

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

0
scharette 30 oct. 2017 a las 03:39

Gotchas

Sí, hay algunas trampas.

fn() == fn(3) == fn(4, 4)

Primero, si fn devuelve 0, no puede saber si se llamó sin ningún parámetro, con un parámetro o con varios parámetros iguales:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

¿Qué significa fn?

Entonces, Python es un lenguaje dinámico. No se especifica en ninguna parte lo que hace fn, cuál debería ser su entrada y cómo debería verse su salida. Por lo tanto, es realmente importante nombrar la función correctamente. Del mismo modo, los argumentos no tienen que llamarse args. delta(*numbers) o calculate_range(*numbers) podrían describir mejor lo que se supone que debe hacer la función.

Errores de argumento

Finalmente, se supone que el operador lógico and evita que la función falle si se llama sin ningún argumento. Sin embargo, todavía falla si algún argumento no es un número:

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Alternativa posible

Aquí hay una manera de escribir la función de acuerdo con la "Es más fácil pedir perdón que permiso". principio:

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

Como ejemplo:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

Si realmente no desea generar una excepción cuando se llama a delta sin ningún argumento, puede devolver algún valor que de otro modo no sería posible (por ejemplo, -1 o None):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1
3
Eric Duminil 30 oct. 2017 a las 15:12

Si. Este es el comportamiento correcto y la comparación.

Al menos en Python, A and B devuelve B si A es esencialmente True incluso si A NO es nulo, NO None NO es un contenedor vacío (como un list, dict, etc., vacío). A se devuelve IFF A es esencialmente False o None o Vacío o Nulo.

Por otro lado, A or B devuelve A si A es esencialmente True incluso si A NO es nulo, NO None NO es un contenedor vacío (como un list, dict, etc. vacío, de lo contrario, devuelve B.

Es fácil no notar (o pasar por alto) este comportamiento porque, en Python, cualquier objeto non-null no vacío evaluado como True se trata como un booleano.

Por ejemplo, todo lo siguiente imprimirá "Verdadero"

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

Por otro lado, todo lo siguiente imprimirá "Falso"

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"
0
emmanuelsa 31 oct. 2017 a las 05:46

Citando de Python Docs

Tenga en cuenta que ni and ni or restringen el valor y tipo que devuelven a False y True, sino que devuelve el último argumento evaluado . Esta a veces es útil, por ejemplo, si s es una cadena que debe reemplazarse por un valor predeterminado si está vacío, 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 con 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 hardcore True o False, Python devuelve Truthy o Falsey , que de todos modos se evaluarán en True o { {X3}}. Podrías usar la expresión tal cual, y seguirá funcionando.


Para saber qué es Verdad y Falsey , consulte la respuesta de Patrick Haugh

15
Amit Joki 30 oct. 2017 a las 05:41

y 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 Ninguno 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.

7
Nithin Varghese 30 oct. 2017 a las 03:55