not - python math operations
Uso extraño del operador "y"/"o" (8)
¿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
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?
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
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 qué 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ía describir mejor lo que se supone que debe hacer la función.
Errores de argumento
Finalmente, se supone que el operador lógico
and
evitará 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 "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, podría 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
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)
[]
- unalist
vacía{}
- undict
vacío()
- unatuple
vacía''''
- unstr
vacíob''''
- unbytes
vacíoset()
- unset
vacío- un
range
vacío, como elrange(0)
- objetos para los cuales
obj.__bool__()
devuelveFalse
obj.__len__()
devuelve0
Un valor "verdadero" satisfará la verificación realizada por las declaraciones
if
owhile
. Usamos "verdad" y "falsedad" para diferenciarnos de los valoresbool
True
yFalse
.
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 a9000
?
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
nior
restringen el valor y el tipo que devuelven aFalse
yTrue
, sino que devuelven el último argumento evaluado . Esto a veces es útil, por ejemplo, sis
es una cadena que debe reemplazarse por un valor predeterminado si está vacía, la expresións 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
Sí. Este es el comportamiento correcto y la comparación.
Al menos en Python,
A and B
devuelven
B
si
A
es esencialmente
True
incluso si
A
NO es Nulo, NO
None
NO es un contenedor vacío (como una
list
vacía,
dict
, etc.).
A
devuelve
A
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 una
list
vacía,
dict
, etc.), 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 verdadero 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"
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.