¿Qué explica la diferencia en el comportamiento de las operaciones booleanas y bit a bit en las listas frente a las matrices NumPy?
Estoy confundido sobre el uso apropiado de &
vs and
en Python, ilustrado en los siguientes ejemplos.
mylist1 = [True, True, True, False, True]
mylist2 = [False, True, False, True, False]
>>> len(mylist1) == len(mylist2)
True
# ---- Example 1 ----
>>> mylist1 and mylist2
[False, True, False, True, False]
# I would have expected [False, True, False, False, False]
# ---- Example 2 ----
>>> mylist1 & mylist2
TypeError: unsupported operand type(s) for &: 'list' and 'list'
# Why not just like example 1?
>>> import numpy as np
# ---- Example 3 ----
>>> np.array(mylist1) and np.array(mylist2)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
# Why not just like Example 4?
# ---- Example 4 ----
>>> np.array(mylist1) & np.array(mylist2)
array([False, True, False, False, False], dtype=bool)
# This is the output I was expecting!
Esta respuesta y esta respuesta me ayudaron a comprender que and
es una operación booleana pero &
es una operación bit a bit.
Leí sobre operaciones bit a bit para comprender mejor el concepto, pero estoy luchando por usar esa información para dar sentido a mis 4 ejemplos anteriores.
El ejemplo 4 me llevó a mi salida deseada, por lo que está bien, pero todavía estoy confundido acerca de cuándo / cómo / por qué debería usar and
vs &
. ¿Por qué las listas y las matrices NumPy se comportan de manera diferente con estos operadores?
¿Alguien puede ayudarme a comprender la diferencia entre las operaciones booleanas y bit a bit para explicar por qué manejan las listas y las matrices NumPy de manera diferente?
np.bitwise_and()
ynp.logical_and()
amigos para evitar confusiones.mylist1 and mylist2
no genera el mismo resultado quemylist2 and mylist1
, ya que lo que se devuelve es la segunda lista como lo señala delnan.Respuestas:
and
prueba si ambas expresiones son lógicamenteTrue
mientras&
(cuando se usa conTrue
/False
valores) prueba si ambas lo sonTrue
.En Python, los objetos incorporados vacíos generalmente se tratan como lógicamente,
False
mientras que los incorporados no vacíos son lógicamenteTrue
. Esto facilita el caso de uso común en el que desea hacer algo si una lista está vacía y otra cosa si la lista no lo está. Tenga en cuenta que esto significa que la lista [False] es lógicamenteTrue
:Entonces, en el Ejemplo 1, la primera lista no está vacía y, por lo tanto
True
, lógicamente , por lo que el valor de verdad deand
la misma es el mismo que el de la segunda lista. (En nuestro caso, la segunda lista no está vacía y, por lo tantoTrue
, lógicamente , pero identificar eso requeriría un paso innecesario de cálculo).Por ejemplo 2, las listas no pueden combinarse significativamente de manera bit a bit porque pueden contener elementos arbitrarios diferentes. Las cosas que se pueden combinar a nivel bit incluyen: Verdades y Falsas, enteros.
Los objetos NumPy, por el contrario, admiten cálculos vectorizados. Es decir, le permiten realizar las mismas operaciones en múltiples datos.
El ejemplo 3 falla porque las matrices NumPy (de longitud> 1) no tienen valor de verdad, ya que esto evita la confusión de lógica basada en vectores.
El ejemplo 4 es simplemente una
and
operación de bits vectorizados .Línea de fondo
Si no está tratando con matrices y no está realizando manipulaciones matemáticas de enteros, probablemente lo desee
and
.Si tiene vectores de valores de verdad que desea combinar, use
numpy
con&
.fuente
Acerca de
list
Primero, un punto muy importante, del que todo seguirá (espero).
En Python ordinario,
list
no es especial de ninguna manera (excepto tener una linda sintaxis para la construcción, que es principalmente un accidente histórico). Una vez que se hace una lista[3,2,6]
, es para todos los propósitos y propósitos solo un objeto Python ordinario, como un número3
, un conjunto{3,7}
o una funciónlambda x: x+5
.(Sí, admite el cambio de sus elementos, y admite la iteración, y muchas otras cosas, pero eso es exactamente lo que es un tipo: admite algunas operaciones, mientras que no admite otras. Int admite elevar a un poder, pero eso no que sea muy especial, es justo lo que es un int. lambda admite llamadas, pero eso no lo hace muy especial, para eso es lambda, después de todo :).
Acerca de
and
and
no es un operador (puede llamarlo "operador", pero también puede llamar "para" un operador :). Los operadores en Python son (implementados mediante) métodos llamados a objetos de algún tipo, generalmente escritos como parte de ese tipo. No hay forma de que un método mantenga una evaluación de algunos de sus operandos, peroand
puede (y debe) hacer eso.La consecuencia de eso es que
and
no se puede sobrecargar, al igualfor
que no se puede sobrecargar. Es completamente general y se comunica a través de un protocolo específico. Lo que puede hacer es personalizar su parte del protocolo, pero eso no significa que pueda alterar el comportamiento deand
completo. El protocolo es:Imagine a Python interpretando "ayb" (esto no sucede literalmente de esta manera, pero ayuda a comprender). Cuando se trata de "y", observa el objeto que acaba de evaluar (a) y le pregunta: ¿es verdad? ( NO : ¿lo eres
True
?) Si eres autor de una clase, puedes personalizar esta respuesta. Sia
responde "no",and
(omite b por completo, no se evalúa en absoluto y) dice:a
es mi resultado ( NO : Falso es mi resultado).Si
a
no responde,and
pregunta: ¿cuál es su longitud? (Nuevamente, puede personalizar esto como autor dea
la clase). Si laa
respuesta es 0,and
hace lo mismo que arriba: lo considera falso ( NO falso), omite by daa
como resultado.Si
a
responde algo distinto de 0 a la segunda pregunta ("cuál es su longitud"), o no responde en absoluto, o responde "sí" a la primera ("¿es verdad?"),and
Evalúa b, y dice:b
es mi resultado. Tenga en cuenta que lo hace NO preguntarb
a cualquier pregunta.La otra forma de decir todo esto es que
a and b
es casi lo mismo queb if a else a
, excepto que a se evalúa solo una vez.Ahora siéntese unos minutos con un bolígrafo y papel, y convénzase que cuando {a, b} es un subconjunto de {Verdadero, Falso}, funciona exactamente como cabría esperar de los operadores booleanos. Pero espero haberte convencido de que es mucho más general y, como verás, mucho más útil de esta manera.
Poniendo esos dos juntos
Ahora espero que entienda su ejemplo 1.
and
no le importa si mylist1 es un número, lista, lambda o un objeto de una clase Argmhbl. Solo le importa la respuesta de mylist1 a las preguntas del protocolo. Y, por supuesto, mylist1 responde 5 a la pregunta sobre la longitud, por lo que devuelve mylist2. Y eso es. No tiene nada que ver con elementos de mylist1 y mylist2: no ingresan a la imagen en ningún lugar.Segundo ejemplo:
&
enlist
Por otro lado,
&
es un operador como cualquier otro, como+
por ejemplo. Se puede definir para un tipo definiendo un método especial en esa clase.int
lo define como bit a bit "y", y bool lo define como lógico "y", pero esa es solo una opción: por ejemplo, los conjuntos y algunos otros objetos, como las vistas de teclas dict, lo definen como una intersección de conjuntos.list
simplemente no lo define, probablemente porque Guido no pensó en ninguna forma obvia de definirlo.numpy
Por otro lado: -D, las matrices numpy son especiales, o al menos están tratando de serlo. Por supuesto, numpy.array es solo una clase, no puede anular
and
de ninguna manera, por lo que hace la siguiente mejor cosa: cuando se le pregunta "¿es verdad?", Numpy.array plantea un ValueError, efectivamente diciendo "por favor reformule la pregunta, mi la visión de la verdad no se ajusta a su modelo ". (Tenga en cuenta que el mensaje de ValueError no hablaand
, porque numpy.array no sabe quién le hace la pregunta; solo habla de la verdad).Para
&
, es una historia completamente diferente. numpy.array puede definirlo como lo desee, y se define de manera&
consistente con otros operadores: pointwise. Entonces finalmente obtienes lo que quieres.HTH
fuente
Los operadores booleanos de cortocircuito (
and
,or
) no se pueden anular porque no hay una forma satisfactoria de hacerlo sin introducir nuevas características del lenguaje o sacrificar el cortocircuito. Como puede saber o no, evalúan el primer operando por su valor de verdad, y dependiendo de ese valor, evalúan y devuelven el segundo argumento, o no evalúan el segundo argumento y devuelven el primero:Tenga en cuenta que se devuelve el (resultado de evaluar el) operando real, no el valor de verdad del mismo.
La única forma de personalizar su comportamiento es anular
__nonzero__
(renombrado__bool__
en Python 3), para que pueda afectar qué operando se devuelve, pero no devolver algo diferente. Las listas (y otras colecciones) se definen como "verdaderas" cuando contienen algo y "falsas" cuando están vacías.Las matrices NumPy rechazan esa noción: para los casos de uso a los que apuntan, son comunes dos nociones diferentes de verdad: (1) Si algún elemento es verdadero y (2) si todos los elementos son verdaderos. Dado que estos dos son completamente (y silenciosamente) incompatibles, y ninguno es claramente más correcto o más común, NumPy se niega a adivinar y requiere que explícitamente use
.any()
o.all()
.&
y|
(ynot
, por cierto) se puede anular por completo, ya que no provocan cortocircuitos. Pueden reemplazar cualquier cosa cuando se reemplazan, y NumPy hace un buen uso de eso para realizar operaciones basadas en elementos, como lo hacen prácticamente con cualquier otra operación escalar. Las listas, por otro lado, no transmiten operaciones a través de sus elementos. Así comomylist1 - mylist2
no significa nada ymylist1 + mylist2
significa algo completamente diferente, no hay&
operador para las listas.fuente
[False] or [True]
evaluar[False]
y[False] and [True]
evaluar[True]
.Ejemplo 1:
Así es como funciona el operador y .
x e y => si x es falso, entonces x , de lo contrario y
En otras palabras, dado
mylist1
que no lo esFalse
, el resultado de la expresión esmylist2
. (Solo se evalúan las listas vacíasFalse
).Ejemplo 2
El
&
operador es por un bit a bit y, como mencionas. Las operaciones bit a bit solo funcionan en números. El resultado de a & b es un número compuesto por 1s en bits que son 1 en a y b . Por ejemplo:Es más fácil ver lo que sucede usando un literal binario (los mismos números que los anteriores):
Las operaciones bit a bit son similares en concepto a las operaciones booleanas (verdad), pero solo funcionan en bits.
Entonces, dado un par de declaraciones sobre mi auto
El "y" lógico de estas dos declaraciones es:
Ambos son ciertos, al menos para mi auto. Entonces, el valor de la declaración como un todo es lógicamente verdadero.
La "y" bit a bit de estas dos declaraciones es un poco más nebulosa:
Si python sabe cómo convertir las declaraciones a valores numéricos, lo hará y calculará los dos valores a nivel de bit. Esto puede llevarlo a creer que
&
es intercambiable conand
, pero como con el ejemplo anterior, son cosas diferentes. Además, para los objetos que no se pueden convertir, solo obtendrá unTypeError
.Ejemplos 3 y 4:
Numpy implementa operaciones aritméticas para matrices:
Pero no implementa operaciones lógicas para matrices, porque no puede sobrecargar operadores lógicos en python . Es por eso que el ejemplo tres no funciona, pero el ejemplo cuatro sí.
Entonces, para responder a su pregunta
and
vs&
: Useand
.Las operaciones bit a bit se utilizan para examinar la estructura de un número (qué bits están establecidos, qué bits no están establecidos). Este tipo de información se usa principalmente en interfaces de sistema operativo de bajo nivel ( bits de permiso de Unix , por ejemplo). La mayoría de los programas de Python no necesitarán saber eso.
Las operaciones lógicas (
and
,or
,not
), sin embargo, se utilizan todo el tiempo.fuente
En Python, una expresión de
X and Y
retornosY
, dado quebool(X) == True
o cualquiera deX
oY
evaluar a False, por ejemplo:El operador bit a bit simplemente no está definido para las listas. Pero se define para enteros, que operan sobre la representación binaria de los números. Considere 16 (01000) y 31 (11111):
NumPy no es un psíquico, no sabe si quiere decir que, por ejemplo,
[False, False]
debería ser igualTrue
en una expresión lógica. En esto, anula un comportamiento estándar de Python, que es: "Cualquier colección vacía conlen(collection) == 0
isFalse
".Probablemente un comportamiento esperado de las matrices y el operador de NumPy.
fuente
Para el primer ejemplo y base en el documento de django
Siempre devolverá la segunda lista, de hecho, una lista no vacía se ve como un valor Verdadero para Python, por lo tanto, Python devuelve el 'último' valor Verdadero para que la segunda lista
fuente
Las operaciones con una lista de Python operan en la lista .
list1 and list2
comprobará silist1
está vacío y devolverálist1
si lo está ylist2
si no lo está.list1 + list2
anexarálist2
alist1
, por lo que obtener una nueva lista conlen(list1) + len(list2)
elementos.Los operadores que solo tienen sentido cuando se aplican por elementos, como
&
, plantean unTypeError
, ya que las operaciones por elementos no son compatibles sin recorrer los elementos.Las matrices Numpy admiten operaciones basadas en elementos .
array1 & array2
calculará el bit a bit o para cada elemento correspondiente enarray1
yarray2
.array1 + array2
calculará la suma de cada elemento correspondiente enarray1
yarray2
.Esto no funciona para
and
yor
.array1 and array2
es esencialmente una abreviatura para el siguiente código:Para esto necesitas una buena definición de
bool(array1)
. Para operaciones globales como las que se usan en las listas de Python, la definición esbool(list) == True
silist
no está vacío yFalse
si está vacío. Para las operaciones con elementos de Numpy, existe cierta desambiguación entre verificar si algún elemento se evalúaTrue
o si todos los elementos se evalúanTrue
. Debido a que ambos son posiblemente correctos, numpy no adivina y genera unValueError
cuándo sebool()
llama (indirectamente) en una matriz.fuente
Buena pregunta. Similar a la observación que tiene sobre los ejemplos 1 y 4 (o debería decir 1 y 4 :)) sobre operadores lógicos
and
bit a bit&
, experimenté ensum
operador. El numpysum
y el py también sesum
comportan de manera diferente. Por ejemplo:Supongamos que "mat" es una matriz nudosa de 5x5 2d como:
Entonces numpy.sum (mat) da la suma total de toda la matriz. Mientras que la suma incorporada de Python como la suma (mat) totaliza solo a lo largo del eje. Vea abajo:
fuente