O que explica a diferença no comportamento de operações booleanas e bit a bit em listas versus matrizes NumPy?
Estou confuso sobre o uso apropriado de &
vs and
em Python, ilustrado nos exemplos a seguir.
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 resposta e esta resposta me ajudaram a entender que and
é uma operação booleana, mas &
é uma operação bit a bit.
Eu li sobre operações bit a bit para entender melhor o conceito, mas estou lutando para usar essas informações para entender meus 4 exemplos acima.
Exemplo 4 me levou à minha saída desejada, de modo que é bom, mas eu ainda estou confuso sobre quando / como / por que eu deveria usar and
vs &
. Por que listas e matrizes NumPy se comportam de maneira diferente com esses operadores?
Alguém pode me ajudar a entender a diferença entre operações booleanas e bit a bit para explicar por que eles manipulam listas e matrizes NumPy de maneira diferente?
np.bitwise_and()
enp.logical_and()
e amigos para evitar confusão.mylist1 and mylist2
não gera o mesmo resultado quemylist2 and mylist1
, pois o que está sendo retornado é a segunda lista, conforme apontado por delnan.Respostas:
and
testa se ambas as expressões são logicamenteTrue
enquanto&
(quando usado comTrue
/False
values) testa se ambas sãoTrue
.No Python, os objetos internos vazios são normalmente tratados como logicamente,
False
enquanto os internos não vazios são logicamenteTrue
. Isso facilita o caso de uso comum em que você deseja fazer algo se uma lista estiver vazia e outra coisa se a lista não estiver. Observe que isso significa que a lista [False] é logicamenteTrue
:Portanto, no Exemplo 1, a primeira lista não está vazia e, portanto
True
, logicamente , portanto, o valor verdadeiro daand
é o mesmo que o da segunda lista. (No nosso caso, a segunda lista não está vazia e, portantoTrue
, logicamente , mas identificar isso exigiria uma etapa de cálculo desnecessária.)Por exemplo 2, as listas não podem ser combinadas significativamente de maneira bit a bit, pois podem conter elementos diferentes arbitrários. As coisas que podem ser combinadas bit a bit incluem: Trues e Falses, números inteiros.
Os objetos NumPy, por outro lado, suportam cálculos vetorizados. Ou seja, eles permitem que você execute as mesmas operações em várias partes de dados.
O exemplo 3 falha porque as matrizes NumPy (de comprimento> 1) não têm valor de verdade, pois isso evita confusão lógica baseada em vetor.
O exemplo 4 é simplesmente uma
and
operação de bit vetorizado .Bottom Line
Se você não está lidando com matrizes e não está realizando manipulações matemáticas de números inteiros, provavelmente deseja
and
.Se você tiver vetores de valores de verdade que deseja combinar, use
numpy
com&
.fonte
Sobre
list
Primeiro, um ponto muito importante, do qual tudo se seguirá (espero).
No Python comum,
list
não é especial de forma alguma (exceto ter uma sintaxe atraente para a construção, que é principalmente um acidente histórico). Depois que uma lista[3,2,6]
é feita, ela é, para todos os efeitos, apenas um objeto Python comum, como um número3
, conjunto{3,7}
ou funçãolambda x: x+5
.(Sim, ele suporta a alteração de seus elementos, e suporta iteração e muitas outras coisas, mas isso é exatamente o que é um tipo: suporta algumas operações, enquanto não suporta outras. Int suporta aumentar a potência, mas isso não Torne-o muito especial - é exatamente o que é um int O lambda suporta chamadas, mas isso não o torna muito especial - é para isso que serve o lambda, afinal :).
Sobre
and
and
não é um operador (você pode chamá-lo de "operador", mas pode chamar "de" um operador também :). Operadores em Python são (implementados por) métodos chamados em objetos de algum tipo, geralmente escritos como parte desse tipo. Não há como um método realizar uma avaliação de alguns de seus operandos, masand
pode (e deve) fazer isso.A conseqüência disso é que
and
não pode ser sobrecarregado, assim comofor
não pode ser sobrecarregado. É completamente geral e se comunica através de um protocolo especificado. O que você pode fazer é personalizar sua parte do protocolo, mas isso não significa que você pode alterarand
completamente o comportamento . O protocolo é:Imagine Python interpretando "aeb" (isso não acontece literalmente dessa maneira, mas ajuda a entender). Quando se trata de "e", olha para o objeto que acabou de avaliar (a) e pergunta: você é verdadeiro? ( NÃO : você é
True
?) Se você é um autor de uma classe, pode personalizar esta resposta. Se aa
resposta for "não",and
(ignora b completamente, não é avaliada e) diz:a
é o meu resultado ( NOT : False é o meu resultado).Se
a
não responde,and
pergunta: qual é o seu comprimento? (Novamente, você pode personalizar isso como um autor daa
classe). Sea
responder 0,and
faz o mesmo que acima - considera falso ( NÃO Falso), pula be dáa
como resultado.Se
a
responder algo diferente de 0 à segunda pergunta ("qual é o seu tamanho"), ou não responder, ou responder "sim" à primeira ("você é verdadeiro"),and
avaliará b e diz:b
é o meu resultado. Observe que ele NÃO fazb
nenhuma pergunta.A outra maneira de dizer tudo isso é
a and b
quase igualb if a else a
, exceto que a é avaliada apenas uma vez.Agora sente-se por alguns minutos com caneta e papel e convença-se de que quando {a, b} é um subconjunto de {True, False}, ele funciona exatamente como você esperaria dos operadores booleanos. Mas espero ter convencido você de que é muito mais geral e, como você verá, muito mais útil dessa maneira.
Juntando esses dois
Agora espero que você entenda o exemplo 1.
and
não se importa se mylist1 é um número, lista, lambda ou um objeto de uma classe Argmhbl. Ele se importa apenas com a resposta da mylist1 às perguntas do protocolo. E, claro, mylist1 responde 5 à pergunta sobre comprimento, e retorna mylist2. E é isso. Não tem nada a ver com os elementos mylist1 e mylist2 - eles não aparecem na imagem em nenhum lugar.Segundo exemplo:
&
emlist
Por outro lado,
&
é um operador como outro qualquer, como+
por exemplo. Pode ser definido para um tipo, definindo um método especial nessa classe.int
define-o como bit a bit "e", e bool define-o como lógico "e", mas isso é apenas uma opção: por exemplo, conjuntos e alguns outros objetos, como visualizações de teclas de ditado, definem-no como uma interseção de conjunto.list
simplesmente não define, provavelmente porque Guido não pensou em nenhuma maneira óbvia de defini-lo.entorpecido
Por outro lado: -D, matrizes numpy são especiais, ou pelo menos estão tentando ser. Obviamente, numpy.array é apenas uma classe, não pode substituir de
and
forma alguma, e faz a melhor coisa: quando perguntado "você é verdadeiro", numpy.array gera um ValueError, dizendo efetivamente "por favor, refaça a pergunta, meu visão da verdade não se encaixa no seu modelo ". (Observe que a mensagem ValueError não falaand
- porque numpy.array não sabe quem está fazendo a pergunta; apenas fala sobre a verdade.)Pois
&
, é uma história completamente diferente. numpy.array pode defini-lo como desejar e define&
consistentemente com outros operadores: pointwise. Então você finalmente consegue o que quer.HTH,
fonte
Os operadores booleanos em curto-circuito (
and
,or
) não podem ser substituídos porque não há uma maneira satisfatória de fazer isso sem introduzir novos recursos de linguagem ou sacrificar o curto-circuito. Como você pode ou não saber, eles avaliam o primeiro operando como seu valor de verdade e, dependendo desse valor, avaliam e retornam o segundo argumento ou não avaliam o segundo argumento e retornam o primeiro:Observe que o resultado (resultado da avaliação do) operando real é retornado, e não o valor verdadeiro.
A única maneira de personalizar seu comportamento é sobrescrever
__nonzero__
(renomeado__bool__
em Python 3), para que você possa afetar qual operando é retornado, mas não retornar algo diferente. As listas (e outras coleções) são definidas como "verdadeiras" quando contêm alguma coisa e "falsey" quando estão vazias.As matrizes NumPy rejeitam essa noção: para os casos de uso que visam, são comuns duas noções diferentes de verdade: (1) se algum elemento é verdadeiro e (2) se todos os elementos são verdadeiros. Como esses dois são completamente (e silenciosamente) incompatíveis, e nenhum é claramente mais correto ou mais comum, o NumPy se recusa a adivinhar e exige que você use explicitamente
.any()
ou.all()
.&
e|
(enot
, a propósito) pode ser totalmente anulado, pois não provoca curto-circuito. Eles podem retornar qualquer coisa quando substituídos, e o NumPy faz bom uso disso para realizar operações com elementos, como acontece com praticamente qualquer outra operação escalar. As listas, por outro lado, não transmitem operações entre seus elementos. Assim comomylist1 - mylist2
não significa nada emylist1 + mylist2
significa algo completamente diferente, não há&
operador para listas.fonte
[False] or [True]
avaliar[False]
e[False] and [True]
avaliar[True]
.Exemplo 1:
É assim que o operador e funciona.
x e y => se x é falso, então x , senão y
Então, em outras palavras, como
mylist1
não éFalse
, o resultado da expressão émylist2
. (Somente listas vazias são avaliadas comoFalse
.)Exemplo 2:
O
&
operador é um pouco bit a bit e, como você mencionou. As operações bit a bit funcionam apenas em números. O resultado de um & b é um número composto de 1s em bits, que são um tanto em um e b . Por exemplo:É mais fácil ver o que está acontecendo usando um literal binário (mesmos números acima):
As operações bit a bit são semelhantes em conceito às operações booleanas (verdade), mas funcionam apenas em bits.
Então, dadas algumas declarações sobre o meu carro
O "e" lógico dessas duas instruções é:
Ambos são verdadeiros, pelo menos para o meu carro. Portanto, o valor da declaração como um todo é logicamente verdadeiro.
O bit a bit "e" dessas duas instruções é um pouco mais nebuloso:
Se o python souber converter as instruções em valores numéricos, o fará e calculará o bit a bit - e os dois valores. Isso pode levar você a acreditar que
&
é intercambiáveland
, mas, como no exemplo acima, são coisas diferentes. Além disso, para os objetos que não podem ser convertidos, você obterá umTypeError
.Exemplos 3 e 4:
O Numpy implementa operações aritméticas para matrizes:
Mas não implementa operações lógicas para matrizes, porque você não pode sobrecarregar operadores lógicos em python . É por isso que o exemplo três não funciona, mas o exemplo quatro funciona.
Então, para responder à sua pergunta
and
vs&
: useand
.As operações bit a bit são usadas para examinar a estrutura de um número (quais bits estão definidos, quais bits não estão definidos). Esse tipo de informação é usado principalmente em interfaces de sistema operacional de baixo nível ( bits de permissão unix , por exemplo). A maioria dos programas python não precisa saber disso.
As operações lógicas (
and
,or
,not
), no entanto, são usados o tempo todo.fonte
Em Python, uma expressão de
X and Y
retornoY
, dado quebool(X) == True
ou qualquer umX
ou éY
avaliado como Falso, por exemplo:O operador bit a bit simplesmente não está definido para listas. Mas é definido para números inteiros - operando sobre a representação binária dos números. Considere 16 (01000) e 31 (11111):
NumPy não é um médium, não sabe, se você quer dizer que, por exemplo,
[False, False]
deve ser igual aTrue
uma expressão lógica. Nele, substitui um comportamento padrão do Python, que é: "Qualquer coleção vazia comlen(collection) == 0
isFalse
".Provavelmente, um comportamento esperado das matrizes e do operador de NumPy.
fonte
Para o primeiro exemplo e base no documento do django,
ele sempre retornará a segunda lista; na verdade, uma lista não vazia é vista como um valor True para Python, portanto, o python retorna o valor 'last' True, de modo que a segunda lista
fonte
As operações com uma lista Python operam na lista .
list1 and list2
verificará selist1
está vazio e retornarálist1
se estiver elist2
se não estiver.list1 + list2
será anexadolist2
alist1
, para que você obtenha uma nova lista comlen(list1) + len(list2)
elementos.Operadores que só fazem sentido quando aplicados em elementos, como
&
, raise aTypeError
, pois operações em elementos não são suportadas sem fazer loop entre os elementos.As matrizes Numpy suportam operações com elementos .
array1 & array2
calculará o bit a bit ou para cada elemento correspondente emarray1
earray2
.array1 + array2
calculará a soma de cada elemento correspondente emarray1
earray2
.Isso não funciona para
and
eor
.array1 and array2
é essencialmente uma abreviação para o seguinte código:Para isso, você precisa de uma boa definição de
bool(array1)
. Para operações globais como as usadas nas listas Python, a definição é quebool(list) == True
selist
não estiver vazio eFalse
se estiver vazio. Para operações de elementos do numpy, existe alguma desambiguação para verificar se algum elemento é avaliadoTrue
ou se todos os elementos são avaliadosTrue
. Como ambos são discutivelmente corretos, o numpy não adivinha e gera umValueError
quandobool()
é (indiretamente) chamado em uma matriz.fonte
Boa pergunta. Semelhante à observação que você tem sobre os exemplos 1 e 4 (ou devo dizer 1 e 4 :)) sobre operadores lógicos de
and
bits&
, experimentei osum
operador. O numpysum
e o py tambémsum
se comportam de maneira diferente. Por exemplo:Suponha que "mat" seja uma matriz numpy 5x5 2d, como:
Então numpy.sum (mat) fornece a soma total de toda a matriz. Enquanto a soma interna do Python, como sum (mat), totaliza apenas ao longo do eixo. Ver abaixo:
fonte