Diferencia entre ‘y’ (booleano) frente a ‘&’ (bitwise) en python. ¿Por qué la diferencia en el comportamiento con las listas frente a las matrices numpy?

¿Qué explica la diferencia en el comportamiento de las operaciones booleanas y bitwise en listas frente a numpy.arrays?

Me estoy confundiendo sobre el uso apropiado de ‘ & ‘ vs ‘ and ‘ en python, ilustrado en los siguientes ejemplos simples.

  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 am confused: I would have expected [False, True, False, False, False] # ---- Example 2 ---- >>>mylist1 & mylist2 *** TypeError: unsupported operand type(s) for &: 'list' and 'list' #I am confused: Why not just like example 1? # ---- Example 3 ---- >>>import numpy as np >>> 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() #I am confused: 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 ‘y’ es una operación booleana pero ‘&’ es una operación a nivel de bits.

Estaba leyendo algo de información para comprender mejor el concepto de operaciones bitwise , pero estoy luchando para usar esa información para dar sentido a mis 4 ejemplos anteriores.

Tenga en cuenta que, en mi situación particular, mi salida deseada es una lista nueva donde:

  len(newlist) == len(mylist1) newlist[i] == (mylist1[i] and mylist2[i]) #for every element of newlist 

El ejemplo 4, arriba, me llevó a mi salida deseada, por lo que está bien.

Pero me siento confundido acerca de cuándo / cómo / por qué debo usar ‘y’ 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 bitwise para explicar por qué manejan las listas y numpy.arrays de manera diferente?

Solo quiero asegurarme de continuar usando estas operaciones correctamente en el futuro. ¡Muchas gracias por la ayuda!

 Numpy version 1.7.1 python 2.7 References all inline with text. 

EDICIONES

1) Gracias a @delnan por señalar que en mis ejemplos originales tenía una ambigüedad que ocultaba mi confusión más profunda. He actualizado mis ejemplos para aclarar mi pregunta.

and prueba si ambas expresiones son lógicamente True mientras que & (cuando se usa con valores True / False ) comprueba si ambas son True .

En Python, los objetos incorporados vacíos generalmente se tratan como lógicamente False mientras que los incorporados no vacíos son lógicamente True . 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 [Falso] es lógicamente True :

 >>> if [False]: ... print 'True' ... True 

Por lo tanto, en el Ejemplo 1, la primera lista no está vacía y, por lo tanto, es lógicamente True , por lo que el valor de verdad de and es el mismo que el de la segunda lista. (En nuestro caso, la segunda lista no está vacía y, por lo tanto, es lógicamente True , pero la identificación requeriría un paso de cálculo innecesario).

Para el ejemplo 2, las listas no se pueden combinar de manera significativa en un modo bit a bit, ya que pueden contener elementos desiguales arbitrarios. Las cosas que se pueden combinar a nivel de bit incluyen: Trues y Falses, enteros.

Los objetos NumPy, por el contrario, admiten cálculos vectorizados. Es decir, le permiten realizar las mismas operaciones en varios datos.

El ejemplo 3 falla porque las matrices NumPy (de longitud> 1) no tienen un valor de verdad, ya que esto evita la confusión de la lógica basada en vectores.

El ejemplo 4 es simplemente un bit vectorizado and operación.

Línea de fondo

  • Si no está tratando con matrices y no está realizando manipulaciones matemáticas de enteros, probablemente desee and .

  • Si tiene vectores de valores de verdad que desea combinar, use numpy con & .

Los operadores booleanos de cortocircuito ( and , or ) no pueden ser anulados debido a que no existe una manera satisfactoria de hacerlo sin introducir nuevas características de lenguaje o sacrificar el cortocircuito. Como puede o no puede saber, 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:

 something_true and x -> x something_false and x -> something_false something_true or x -> something_true something_false or x -> x 

Tenga en cuenta que se devuelve el (resultado de evaluar el) operando real, no su valor real.

La única forma de personalizar su comportamiento es anular __nonzero__ (renombrado a __bool__ en Python 3), para que pueda afectar a qué operando se devuelve, pero no devolver algo diferente. Las listas (y otras colecciones) se definen como “sinceras” cuando contienen algo y “falsey” 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. Ya 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 uses explícitamente .any() o .all() .

& y | (y not , por cierto) se puede anular completamente, ya que no cortocircuitan. Pueden devolver cualquier cosa cuando se anulan, y NumPy hace un buen uso de eso para realizar operaciones de elementos, como lo hace con prácticamente cualquier otra operación escalar. Las listas, por otro lado, no transmiten operaciones a través de sus elementos. Al igual que mylist1 - mylist2 no significa nada y mylist1 + mylist2 significa algo completamente diferente, no hay un operador & para listas.

Acerca de la list

Primero un punto muy importante, desde el cual todo saldrá (espero).

En Python ordinario, la list no es especial de ninguna manera (excepto que tiene una syntax linda 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úmero 3 , conjunto {3,7} , o una función lambda x: x+5 .

(Sí, admite el cambio de sus elementos y 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 boost la potencia, pero eso no hazlo muy especial: es exactamente lo que es un int. lambda admite las 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 puede llamar “para” un operador también :). Los operadores en Python son (implementados a través de) métodos llamados en objetos de algún tipo, generalmente escritos como parte de ese tipo. No hay forma de que un método lleve a cabo una evaluación de algunos de sus operandos, pero puede (y debe) hacer eso.

La consecuencia de eso es que and no se puede sobrecargar, al igual que for 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 completamente. El protocolo es:

Imagine que Python interpreta “a y b” (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 lo pregunta: ¿eres verdad? ( NO : ¿eres True ?) Si eres un autor de una clase, puedes personalizar esta respuesta. Si la respuesta es “no”, and (omite b por completo, no se evalúa en absoluto, y) dice: a es mi resultado ( NO : Falso es mi resultado).

Si no responde and pregunta: ¿cuál es tu longitud? (Nuevamente, puede personalizar esto como un autor de la clase de un). Si a 0 responde and hace lo mismo que el anterior, lo considera falso ( NO Falso), omite b y da a resultado.

Si a responde algo distinto de 0 a la segunda pregunta (“cuál es tu longitud”), o no responde en absoluto, o responde “sí” a la primera (“eres verdadero”), and evalúa b , y dice: b es mi resultado. Tenga en cuenta que NO hace b ninguna pregunta.

La otra forma de decir todo esto es que a and b es casi lo mismo que b if a else a , excepto que a se evalúa solo una vez.

Ahora siéntese por unos minutos con un lápiz y papel, y convénzase de que cuando {a, b} es un subconjunto de {Verdadero, Falso}, funciona exactamente como lo esperaría 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 a esos dos juntos

Ahora espero que entiendas tu ejemplo 1. and no te 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, y devuelve mylist2. Y eso es. No tiene nada que ver con los elementos de mylist1 y mylist2, ya que no entran en la imagen en ningún lugar.

Segundo ejemplo: & en la list

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 define como bit a bit “and”, y bool lo define como lógico “and”, pero esa es solo una opción: por ejemplo, los conjuntos y algunos otros objetos como las vistas de las 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.

adormecido

En la otra pierna: -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 pregunte “¿es verdad?” Mi visión de la verdad no encaja en tu modelo “. (Tenga en cuenta que el mensaje ValueError no habla acerca de and – porque numpy.array no sabe quién le está haciendo la pregunta; solo habla sobre la verdad).

Para & , es una historia completamente diferente. numpy.array puede definirlo como desee, y lo define & coherente con otros operadores: pointwise. Así que finalmente obtienes lo que quieres.

HTH,

Ejemplo 1:

Así es como funciona el operador y .

x e y => si x es falso, entonces x , si no y

En otras palabras, dado que mylist1 no es False , el resultado de la expresión es mylist2 . (Solo las listas vacías se evalúan como False ).

Ejemplo 2:

El operador & es para un bit y, como usted menciona. Las operaciones bitwise solo funcionan con números. El resultado de a & b es un número compuesto de 1s en bits que son 1 en a y b . Por ejemplo:

 >>> 3 & 1 1 

Es más fácil ver lo que está sucediendo usando un literal binario (los mismos números que arriba):

 >>> 0b0011 & 0b0001 0b0001 

Las operaciones bitwise son similares en concepto a las operaciones booleanas (verdad), pero funcionan solo en bits.

Así que, dadas un par de declaraciones sobre mi coche.

  1. Mi coche es rojo
  2. Mi carro tiene ruedas

El “y” lógico de estas dos afirmaciones es:

(¿mi coche es rojo?) y (¿el coche tiene ruedas?) => verdadero lógico o falso

Ambos son ciertos, al menos para mi coche. Así que el valor de la statement en su conjunto es lógicamente verdadero.

El bit a bit “y” de estas dos afirmaciones es un poco más nebuloso:

(el valor numérico de la statement ‘mi auto es rojo’) y (el valor numérico de la statement ‘mi auto tiene ruedas’) => número

Si Python sabe cómo convertir las declaraciones a valores numéricos, lo hará y calculará el bit a bit y los dos valores. Esto puede llevarlo a creer que & es intercambiable con and , pero al igual que con el ejemplo anterior, son cosas diferentes. Además, para los objetos que no se pueden convertir, solo obtendrá un TypeError .

Ejemplo 3 y 4:

Numpy implementa operaciones aritméticas para matrices:

Las operaciones aritméticas y de comparación en ndarrays se definen como operaciones de elementos, y generalmente producen objetos ndarray como resultados.

Pero no implementa operaciones lógicas para matrices, porque no puede sobrecargar los operadores lógicos en python . Es por eso que el ejemplo tres no funciona, pero el ejemplo cuatro sí funciona.

Así que para responder a tu and vs & pregunta: Usa and .

Las operaciones bitwise se usan para examinar la estructura de un número (qué bits se establecen, qué bits no se establecen). Este tipo de información se usa principalmente en interfaces de sistemas operativos de bajo nivel ( bits de permiso de Unix , por ejemplo). La mayoría de los progtwigs de Python no necesitarán saber eso.

Las operaciones lógicas ( and , or , not ), sin embargo, se utilizan todo el tiempo.

  1. En Python, una expresión de X and Y devuelve Y , dado que bool(X) == True o cualquiera de X o Y evalúa como False, por ejemplo:

     True and 20 >>> 20 False and 20 >>> False 20 and [] >>> [] 
  2. El operador bitwise simplemente no está definido para listas. Pero se define para enteros, que operan sobre la representación binaria de los números. Considere 16 (01000) y 31 (11111):

     16 & 31 >>> 16 
  3. NumPy no es un psíquico, no sabe si quiere decir que, por ejemplo, [False, False] debe ser igual a True en una expresión lógica. En esto, anula un comportamiento estándar de Python, que es: “Cualquier colección vacía con len(collection) == 0 es False “.

  4. Probablemente un comportamiento esperado de los arreglos y operadores de NumPy.

Para el primer ejemplo y base en el documento de django.
Siempre devolverá la segunda lista, de hecho, una lista no vacía se verá como un valor Verdadero para Python, por lo tanto, Python devolverá el ‘último’ Valor verdadero y la segunda lista

 In [74]: mylist1 = [False] In [75]: mylist2 = [False, True, False, True, False] In [76]: mylist1 and mylist2 Out[76]: [False, True, False, True, False] In [77]: mylist2 and mylist1 Out[77]: [False] 

Las operaciones con una lista de Python operan en la lista . list1 and list2 comprobarán si list1 está vacío, y devolverán list1 si lo está, y list2 si no lo está. list1 + list2 agregará list2 a list1 , así obtendrá una nueva lista con los elementos len(list1) + len(list2) .

Los operadores que solo tienen sentido cuando se aplican en forma de elementos, como & , TypeError un TypeError , ya que las operaciones en forma de elementos no son compatibles sin hacer un bucle a través de los elementos.

Las matrices numpy son compatibles con operaciones de elementos . array1 & array2 calculará el bit a bit o para cada elemento correspondiente en array1 y array2 . array1 + array2 calculará la sum de cada elemento correspondiente en array1 y array2 .

Esto no funciona para and y or .

array1 and array2 es esencialmente una mano corta para el siguiente código:

 if bool(array1): return array2 else: return array1 

Para esto necesitas una buena definición de bool(array1) . Para operaciones globales como las utilizadas en las listas de Python, la definición es que bool(list) == True si la list no está vacía, y False si está vacía. Para las operaciones de numpy en lo que respecta a los elementos, hay cierta falta de ambigüedad en verificar si algún elemento se evalúa como True o si todos los elementos se evalúan como True . Debido a que ambos son posiblemente correctos, numpy no adivina y genera un ValueError cuando se llama (indirectamente) a bool() en una matriz.