'and' (boolean) vs '&' (bitwise) - Perché la differenza di comportamento con gli elenchi rispetto agli array intorpiditi?


142

Cosa spiega la differenza nel comportamento delle operazioni booleane e bit a bit sugli elenchi rispetto agli array NumPy?

Sono confuso sull'uso appropriato di &vs andin Python, illustrato nei seguenti esempi.

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!

Questa risposta e questa risposta mi hanno aiutato a capire che si andtratta di un'operazione booleana ma &è un'operazione bitwise.

Ho letto delle operazioni bit per capire meglio il concetto, ma sto lottando per usare quell'informazione per dare un senso ai miei precedenti 4 esempi.

ESEMPIO 4 mi ha portato alla mia uscita desiderata, in modo che va bene, ma io sono ancora confuso su quando / come / perché dovrei usare andvs &. Perché gli elenchi e gli array NumPy si comportano diversamente con questi operatori?

Qualcuno può aiutarmi a capire la differenza tra operazioni booleane e bit per spiegare perché gestiscono gli elenchi e le matrici NumPy in modo diverso?


2
In Numpy c'è np.bitwise_and()e np.logical_and()e gli amici per evitare confusione.
Dietrich,

1
Nell'esempio 1, mylist1 and mylist2non viene prodotto lo stesso risultato di mylist2 and mylist1, poiché ciò che viene restituito è il secondo elenco, come sottolineato da Delnan.
user2015487,

Risposte:


114

andverifica se entrambe le espressioni sono logicamente Truementre &(se utilizzato con True/ Falsevalori) verifica se entrambe lo sono True.

In Python, gli oggetti incorporati vuoti vengono generalmente trattati come logicamente, Falsementre quelli incorporati non vuoti sono logicamente True. Questo facilita il caso d'uso comune in cui si desidera fare qualcosa se un elenco è vuoto e qualcos'altro se l'elenco non lo è. Si noti che ciò significa che l'elenco [Falso] è logicamente True:

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

Quindi, nell'esempio 1, il primo elenco non è vuoto e quindi logicamente True, quindi il valore di verità andè uguale a quello del secondo elenco. (Nel nostro caso, il secondo elenco è non vuoto e quindi logicamente True, ma identificando ciò richiederebbe una fase di calcolo non necessaria.)

Ad esempio 2, le liste non possono essere significativamente combinate in modo bit a bit perché possono contenere elementi arbitrari a differenza. Le cose che possono essere combinate bit a bit includono: Veri e falsi, numeri interi.

Gli oggetti NumPy, al contrario, supportano calcoli vettoriali. Cioè, ti consentono di eseguire le stesse operazioni su più pezzi di dati.

L'esempio 3 ha esito negativo poiché le matrici NumPy (di lunghezza> 1) non hanno alcun valore di verità in quanto ciò impedisce la confusione logica basata sul vettore.

L'esempio 4 è semplicemente un'operazione di bit vettorializzata and.

Linea di fondo

  • Se non si ha a che fare con array e non si eseguono manipolazioni matematiche di numeri interi, probabilmente lo si desidera and.

  • Se hai vettori di valori di verità che desideri combinare, usa numpycon &.


27

Di list

Innanzitutto un punto molto importante, dal quale tutto seguirà (spero).

In Python ordinario, listnon è speciale in alcun modo (tranne che per avere una sintassi carina per la costruzione, che è principalmente un incidente storico). Una volta [3,2,6]creato un elenco , è a tutti gli effetti solo un normale oggetto Python, come un numero 3, un set {3,7}o una funzione lambda x: x+5.

(Sì, supporta la modifica dei suoi elementi e supporta l'iterazione e molte altre cose, ma è proprio quello che è un tipo: supporta alcune operazioni, mentre non supporta altre. Int supporta l'innalzamento a un potere, ma questo non rendilo molto speciale - è proprio quello che è un int. lambda supporta la chiamata, ma questo non lo rende molto speciale - questo è ciò che Lambda è, dopo tutto :).

Di and

andnon è un operatore (puoi chiamarlo "operatore", ma puoi anche chiamare "per" un operatore :). Gli operatori in Python sono metodi (implementati attraverso) chiamati su oggetti di qualche tipo, generalmente scritti come parte di quel tipo. Non c'è modo per un metodo di tenere una valutazione di alcuni dei suoi operandi, ma andpuò (e deve) farlo.

La conseguenza di ciò è che andnon può essere sovraccaricato, proprio come fornon può essere sovraccaricato. È completamente generale e comunica attraverso un protocollo specificato. Quello che puoi fare è personalizzare la tua parte del protocollo, ma ciò non significa che puoi modificare andcompletamente il comportamento . Il protocollo è:

Immagina Python che interpreta "aeb" (questo non accade letteralmente in questo modo, ma aiuta a capire). Quando si tratta di "e", guarda l'oggetto che ha appena valutato (a), e gli chiede: sei vero? ( NON : lo sei True?) Se sei un autore di una classe, puoi personalizzare questa risposta. Se la arisposta "no", and(salta completamente b, non viene valutata affatto e) dice: aè il mio risultato ( NON : Falso è il mio risultato).

Se anon risponde, andchiede: qual è la tua lunghezza? (Ancora una volta, puoi personalizzarlo come autore della aclasse). Se arisponde 0, andfa lo stesso come sopra - lo considera falso ( NON falso), salta b e dà acome risultato.

Se arisponde a una domanda diversa da 0 alla seconda domanda ("qual è la tua lunghezza"), oppure non risponde affatto o risponde "sì" alla prima ("sei vero"), andvaluta b e dice: bè il mio risultato. Si noti che lo fa non chiedere ba qualsiasi domanda.

L'altro modo di dire tutto ciò è a and bquasi lo stesso b if a else a, tranne che a viene valutato una sola volta.

Ora siediti qualche minuto con carta e penna e convinciti che quando {a, b} è un sottoinsieme di {True, False}, funziona esattamente come ti aspetteresti dagli operatori booleani. Ma spero di averti convinto che è molto più generale e, come vedrai, molto più utile in questo modo.

Mettendo insieme quei due

Ora spero che tu capisca il tuo esempio 1. andnon importa se mylist1 è un numero, elenco, lambda o un oggetto di una classe Argmhbl. Si preoccupa solo della risposta di mylist1 alle domande del protocollo. E, naturalmente, mylist1 risponde 5 alla domanda sulla lunghezza, quindi restituisce mylist2. E questo è tutto. Non ha nulla a che fare con gli elementi di mylist1 e mylist2: non entrano nell'immagine da nessuna parte.

Secondo esempio: &onlist

D'altra parte, &è un operatore come un altro, come +ad esempio. Può essere definito per un tipo definendo un metodo speciale su quella classe. intlo definisce come bit per bit "e", e bool lo definisce come logico "e", ma questa è solo un'opzione: per esempio, set e alcuni altri oggetti come le viste dei tasti dict lo definiscono come un incrocio impostato. listsemplicemente non lo definisce, probabilmente perché Guido non ha pensato a un modo ovvio per definirlo.

numpy

Dall'altra parte MrGreen, gli array intorpiditi sono speciali, o almeno stanno cercando di esserlo. Ovviamente numpy.array è solo una classe, non può ignorare andin alcun modo, quindi fa la cosa migliore successiva: quando viene chiesto "sei vero", numpy.array genera un ValueError, dicendo efficacemente "per favore riformula la domanda, mio la visione della verità non rientra nel tuo modello ". (Nota che il messaggio ValueError non parla di and- perché numpy.array non sa chi lo sta ponendo la domanda; parla solo di verità.)

Perché &è una storia completamente diversa. numpy.array può definirlo come desidera e definisce &coerentemente con altri operatori: in senso puntuale. Quindi finalmente ottieni quello che vuoi.

HTH,


23

Gli operatori booleani in corto circuito ( and, or) non possono essere sostituiti perché non esiste un modo soddisfacente per farlo senza introdurre nuove funzionalità linguistiche o sacrificare il corto circuito. Come forse saprai, valutano il primo operando per il suo valore di verità e, a seconda di quel valore, valutano e restituiscono il secondo argomento o non valutano il secondo argomento e restituiscono il primo:

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

Si noti che viene restituito l'operando effettivo (risultato della valutazione), non il suo valore di verità.

L'unico modo per personalizzare il loro comportamento è quello di sovrascrivere __nonzero__(rinominato __bool__in Python 3), quindi puoi influenzare quale operando viene restituito, ma non restituire qualcosa di diverso. Le liste (e altre raccolte) sono definite "verità" quando contengono qualcosa e "falsità" quando sono vuote.

Le matrici NumPy respingono questa nozione: per i casi d'uso a cui mirano, due diverse nozioni di verità sono comuni: (1) se ogni elemento è vero e (2) se tutti gli elementi sono veri. Poiché questi due sono completamente (e silenziosamente) incompatibili, e nessuno dei due è chiaramente più corretto o più comune, NumPy rifiuta di indovinare e richiede di usare esplicitamente .any()o .all().

&e |(e not, a proposito) possono essere completamente sostituiti, poiché non cortocircuitano. Possono restituire qualsiasi cosa quando vengono sostituiti e NumPy ne fa buon uso per eseguire operazioni sagge agli elementi, come fanno praticamente con qualsiasi altra operazione scalare. Gli elenchi, d'altra parte, non trasmettono operazioni attraverso i loro elementi. Proprio come mylist1 - mylist2non significa nulla e mylist1 + mylist2significa qualcosa di completamente diverso, non esiste un &operatore per le liste.


3
Un esempio particolarmente interessante di ciò che questo può produrre è la [False] or [True]valutazione [False]e la [False] and [True]valutazione [True].
Rob Watts,

16

Esempio 1:

Ecco come funziona l' operatore e .

x ed y => se x è falso, allora x , altrimenti y

Quindi, in altre parole, poiché mylist1non lo è False, il risultato dell'espressione è mylist2. (Solo gli elenchi vuoti valutano False.)

Esempio 2:

L' &operatore è per un po 'e, come dici tu. Le operazioni bit per bit funzionano solo sui numeri. Il risultato di a & b è un numero composto da 1s in bit che sono 1 sia in a che in b . Per esempio:

>>> 3 & 1
1

È più facile vedere cosa sta succedendo usando un letterale binario (stessi numeri come sopra):

>>> 0b0011 & 0b0001
0b0001

Le operazioni bit a bit sono simili nel concetto alle operazioni booleane (verità), ma funzionano solo sui bit.

Quindi, dato un paio di dichiarazioni sulla mia macchina

  1. La mia macchina è rossa
  2. La mia macchina ha le ruote

Il "e" logico di queste due affermazioni è:

(la mia macchina è rossa?) e (l'auto ha le ruote?) => logico vero di falso valore

Entrambi sono veri, almeno per la mia macchina. Quindi il valore dell'istruzione nel suo insieme è logicamente vero.

Il bit per bit "e" di queste due affermazioni è un po 'più nebuloso:

(il valore numerico della frase "la mia macchina è rossa") & (il valore numerico della frase "la mia macchina ha le ruote") => numero

Se python sa come convertire le istruzioni in valori numerici, lo farà e calcolerà bit a bit e dei due valori. Questo può farti credere che &sia intercambiabile con and, ma come nell'esempio precedente sono cose diverse. Inoltre, per gli oggetti che non possono essere convertiti, otterrai solo un TypeError.

Esempio 3 e 4:

Numpy implementa operazioni aritmetiche per array:

Le operazioni aritmetiche e di confronto su ndarrays sono definite come operazioni sagge agli elementi e generalmente danno come risultato oggetti ndarray.

Ma non implementa operazioni logiche per array, perché non è possibile sovraccaricare gli operatori logici in Python . Ecco perché l'esempio tre non funziona, ma l'esempio quattro funziona.

Quindi, per rispondere alla tua domanda andvs &: Usa and.

Le operazioni bit a bit vengono utilizzate per esaminare la struttura di un numero (quali bit sono impostati, quali bit non sono impostati). Questo tipo di informazioni viene utilizzato principalmente nelle interfacce del sistema operativo di basso livello ( bit di autorizzazione unix , ad esempio). La maggior parte dei programmi Python non avrà bisogno di saperlo.

Le operazioni logiche ( and, or, not), tuttavia, sono utilizzati per tutto il tempo.


14
  1. In Python un'espressione di X and Yritorni Y, dato che bool(X) == Trueo uno qualsiasi Xo Yvaluta Falso, ad esempio:

    True and 20 
    >>> 20
    
    False and 20
    >>> False
    
    20 and []
    >>> []
  2. L'operatore Bitwise non è semplicemente definito per gli elenchi. Ma è definito per numeri interi, che operano sulla rappresentazione binaria dei numeri. Considera 16 (01000) e 31 (11111):

    16 & 31
    >>> 16
  3. NumPy non è un sensitivo, non sa se intendi che ad esempio [False, False]dovrebbe essere uguale Truein un'espressione logica. In questo sostituisce un comportamento Python standard, che è: "Qualsiasi raccolta vuota con len(collection) == 0è False".

  4. Probabilmente un comportamento previsto delle matrici e dell'operatore di NumPy.


False e 20 restituisce False
Rahul il

4

Per il primo esempio e basato sul documento del django
Restituirà sempre il secondo elenco, infatti un elenco non vuoto viene visto come un valore True per Python, quindi python restituisce il valore 'last' True quindi il secondo elenco

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]

4

Le operazioni con un elenco Python operano nell'elenco . list1 and list2controllerà se list1è vuoto e restituirà list1se lo è e list2se non lo è. list1 + list2verrà aggiunto list2a list1, in modo da ottenere un nuovo elenco con len(list1) + len(list2)elementi.

Gli operatori che hanno senso solo se applicati a livello di elemento, ad esempio &, aumentano a TypeError, poiché le operazioni a livello di elemento non sono supportate senza scorrere gli elementi.

Array NumPy supportano elemento-wise operazioni. array1 & array2calcolerà bit per bit o per ogni elemento corrispondente in array1e array2. array1 + array2calcolerà la somma per ogni elemento corrispondente in array1e array2.

Questo non funziona per ande or.

array1 and array2 è essenzialmente una scorciatoia per il seguente codice:

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

Per questo è necessaria una buona definizione di bool(array1). Per operazioni globali come quelle utilizzate negli elenchi Python, la definizione è che bool(list) == Truese listnon è vuoto e Falsese è vuoto. Per le operazioni dal punto di vista degli elementi di numpy, vi è una certa ambiguità nel controllare se un elemento viene valutato Trueo se tutti gli elementi lo valutano True. Poiché entrambi sono probabilmente corretti, numpy non indovina e genera un ValueErrorquando bool()viene (indirettamente) chiamato su un array.


0

Buona domanda. Simile all'osservazione che hai sugli esempi 1 e 4 (o dovrei dire 1 e 4 :)) su operatori logici andbit a bit &, ho sperimentato su sumoperatore. Anche il numpy sume il py si sumcomportano diversamente. Per esempio:

Supponiamo che "mat" sia un array intorpidito 5x5 2d come:

array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20],
       [21, 22, 23, 24, 25]])

Quindi numpy.sum (mat) fornisce la somma totale dell'intera matrice. Considerando che la somma incorporata da Python come sum (mat) è totale solo lungo l'asse. Vedi sotto:

np.sum(mat)  ## --> gives 325
sum(mat)     ## --> gives array([55, 60, 65, 70, 75])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.