'und' (boolesch) vs '&' (bitweise) - Warum Verhaltensunterschiede zwischen Listen und numpy Arrays?


141

Was erklärt den Unterschied im Verhalten von booleschen und bitweisen Operationen in Listen gegenüber NumPy-Arrays?

Ich bin verwirrt über die angemessene Verwendung von &vs andin Python, die in den folgenden Beispielen veranschaulicht wird.

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!

Diese Antwort und diese Antwort haben mir geholfen zu verstehen, dass andes sich um eine boolesche Operation handelt, aber &um eine bitweise Operation.

Ich habe über bitweise Operationen gelesen, um das Konzept besser zu verstehen, aber ich habe Schwierigkeiten, diese Informationen zu verwenden, um meine obigen 4 Beispiele zu verstehen.

Beispiel 4 führte mich zu meiner gewünschten Ausgabe, das ist in Ordnung, aber ich bin immer noch verwirrt darüber, wann / wie / warum ich andvs verwenden sollte &. Warum verhalten sich Listen und NumPy-Arrays bei diesen Operatoren unterschiedlich?

Kann mir jemand helfen, den Unterschied zwischen booleschen und bitweisen Operationen zu verstehen, um zu erklären, warum sie Listen und NumPy-Arrays unterschiedlich behandeln?


2
In Numpy gibt es np.bitwise_and()und np.logical_and()und Freunde, um Verwirrung zu vermeiden.
Dietrich

1
In Beispiel 1 mylist1 and mylist2wird nicht das gleiche Ergebnis ausgegeben wie mylist2 and mylist1, da die zweite Liste zurückgegeben wird, auf die delnan hingewiesen hat.
user2015487

Antworten:


113

andtestet, ob beide Ausdrücke logisch sind, Truewährend &(wenn mit True/ Falsevalues ​​verwendet) getestet wird, ob beide logisch sind True.

In Python werden leere integrierte Objekte normalerweise als logisch behandelt, Falsewährend nicht leere integrierte Objekte logisch sind True. Dies erleichtert den allgemeinen Anwendungsfall, in dem Sie etwas tun möchten, wenn eine Liste leer ist, und etwas anderes, wenn die Liste nicht leer ist. Beachten Sie, dass dies bedeutet, dass die Liste [False] logisch ist True:

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

In Beispiel 1 ist die erste Liste also nicht leer und daher logisch True, sodass der Wahrheitswert von andder gleiche ist wie der der zweiten Liste. (In unserem Fall ist die zweite Liste nicht leer und daher logisch True, aber die Identifizierung würde einen unnötigen Berechnungsschritt erfordern.)

Zum Beispiel 2 können Listen nicht sinnvoll bitweise kombiniert werden, da sie beliebige Elemente enthalten können. Zu den Dingen, die bitweise kombiniert werden können, gehören: Trues und Falses, ganze Zahlen.

Im Gegensatz dazu unterstützen NumPy-Objekte vektorisierte Berechnungen. Das heißt, Sie können dieselben Vorgänge für mehrere Daten ausführen.

Beispiel 3 schlägt fehl, weil NumPy-Arrays (mit einer Länge> 1) keinen Wahrheitswert haben, da dies eine vektorbasierte logische Verwirrung verhindert.

Beispiel 4 ist einfach eine vektorisierte Bitoperation and.

Endeffekt

  • Wenn Sie sich nicht mit Arrays beschäftigen und keine mathematischen Manipulationen von ganzen Zahlen durchführen, möchten Sie wahrscheinlich and.

  • Wenn Sie Vektoren von Wahrheitswerten haben, die Sie kombinieren möchten, verwenden Sie numpymit &.


26

Über list

Zunächst ein sehr wichtiger Punkt, von dem aus alles folgen wird (hoffe ich).

In gewöhnlichem Python listist es in keiner Weise etwas Besonderes (außer eine niedliche Syntax zum Konstruieren zu haben, was meistens ein historischer Unfall ist). Sobald eine Liste [3,2,6]erstellt wurde, handelt es sich in jeder Hinsicht nur um ein gewöhnliches Python-Objekt, z. B. eine Zahl 3, eine Menge {3,7}oder eine Funktion lambda x: x+5.

(Ja, es unterstützt das Ändern seiner Elemente und es unterstützt die Iteration und viele andere Dinge, aber genau das ist ein Typ: Es unterstützt einige Operationen, während andere nicht unterstützt werden. Int unterstützt das Erhöhen auf eine Potenz, aber das tut es nicht mach es zu etwas ganz Besonderem - es ist genau das, was ein Int ist. Lambda unterstützt das Aufrufen, aber das macht es nicht zu etwas Besonderem - dafür ist Lambda schließlich :).

Über and

andist kein Operator (Sie können es "Operator" nennen, aber Sie können auch "für" einen Operator nennen :). Operatoren in Python sind (implementiert durch) Methoden, die für Objekte eines Typs aufgerufen werden und normalerweise als Teil dieses Typs geschrieben werden. Es gibt keine Möglichkeit für eine Methode, eine Auswertung einiger ihrer Operanden durchzuführen, aber sie andkann (und muss) dies tun.

Die Folge davon ist, dass andnicht überladen werden kann, genauso wie fornicht überladen werden kann. Es ist vollständig allgemein und kommuniziert über ein bestimmtes Protokoll. Was Sie tun können , ist Ihren Teil des Protokolls anzupassen, aber das bedeutet nicht, dass Sie das Verhalten von andvollständig ändern können . Das Protokoll lautet:

Stellen Sie sich vor, Python interpretiert "a und b" (dies geschieht nicht wörtlich auf diese Weise, aber es hilft beim Verständnis). Wenn es um "und" geht, betrachtet es das Objekt, das es gerade ausgewertet hat (a), und fragt es: Sind Sie wahr? ( NICHT : bist du True?) Wenn du ein Autor der Klasse von a bist, kannst du diese Antwort anpassen. Wenn die aAntwort "nein" andlautet (b wird vollständig übersprungen, wird es überhaupt nicht ausgewertet und) lautet: aist mein Ergebnis ( NICHT : Falsch ist mein Ergebnis).

Wenn anicht antwortet, andfragt es: Wie lang sind Sie? (Auch hier können Sie dies als Autor der aKlasse anpassen .) Wenn die aAntwort 0 lautet, andwird dasselbe wie oben ausgeführt - betrachtet sie als falsch ( NICHT falsch), überspringt b und gibt aals Ergebnis.

Wenn adie zweite Frage mit etwas anderem als 0 beantwortet wird ("Was ist Ihre Länge?") Oder wenn die erste Frage überhaupt nicht beantwortet wird oder wenn die erste Frage mit "Ja" beantwortet wird (" Sind Sie wahr?"), andWird b und bewertet sagt: bist mein Ergebnis. Beachten Sie, dass es sich nicht fragen bFragen.

Die andere Art, dies alles zu sagen, a and bist fast die gleiche wie b if a else a, außer dass a nur einmal ausgewertet wird.

Setzen Sie sich nun ein paar Minuten mit Stift und Papier hin und überzeugen Sie sich selbst, dass {a, b}, wenn es sich um eine Teilmenge von {True, False} handelt, genau so funktioniert, wie Sie es von Booleschen Operatoren erwarten würden. Aber ich hoffe, ich habe Sie davon überzeugt, dass es viel allgemeiner und, wie Sie sehen werden, auf diese Weise viel nützlicher ist.

Diese beiden zusammenfügen

Jetzt hoffe ich, dass Sie Ihr Beispiel verstehen 1. andEs ist mir egal, ob mylist1 eine Zahl, eine Liste, ein Lambda oder ein Objekt einer Klasse Argmhbl ist. Es geht nur um die Antwort von mylist1 auf die Fragen des Protokolls. Und natürlich beantwortet mylist1 5 auf die Frage nach der Länge, also gibt mylist2 zurück. Und das ist es. Es hat nichts mit Elementen von mylist1 und mylist2 zu tun - sie kommen nirgendwo ins Bild.

Zweites Beispiel: &einlist

Auf der anderen Seite &ist ein Operator wie jeder andere, wie +zum Beispiel. Sie kann für einen Typ definiert werden, indem eine spezielle Methode für diese Klasse definiert wird. intdefiniert es als bitweise "und" und bool definiert es als logisch "und", aber das ist nur eine Option: Zum Beispiel definieren Mengen und einige andere Objekte wie Diktatschlüsselansichten es als Mengenschnittstelle. listdefiniert es einfach nicht, wahrscheinlich weil Guido sich keine offensichtliche Art der Definition ausgedacht hat.

numpy

Auf der anderen Seite: -D, numpy Arrays sind etwas Besonderes oder versuchen es zumindest. Natürlich ist numpy.array nur eine Klasse, sie kann andin keiner Weise überschrieben werden, also macht sie das nächstbeste: Wenn sie gefragt werden, ob Sie wahr sind, löst numpy.array einen ValueError aus und sagt effektiv: "Bitte formulieren Sie die Frage neu, meine." Sicht der Wahrheit passt nicht in Ihr Modell ". (Beachten Sie, dass in der ValueError-Nachricht nicht gesprochen wird, andda numpy.array nicht weiß, wer die Frage stellt. Sie spricht nur über die Wahrheit.)

Denn &es ist eine ganz andere Geschichte. numpy.array kann es so definieren, wie es möchte, und es definiert &konsistent mit anderen Operatoren: pointwise. So bekommen Sie endlich, was Sie wollen.

HTH,


23

Die kurzgeschlossenen booleschen Operatoren ( and, or) können nicht überschrieben werden, da es keinen zufriedenstellenden Weg gibt, dies zu tun, ohne neue Sprachfunktionen einzuführen oder Kurzschlüsse zu opfern. Wie Sie vielleicht wissen oder nicht wissen, bewerten sie den ersten Operanden auf seinen Wahrheitswert. Abhängig von diesem Wert bewerten sie entweder das zweite Argument und geben es zurück oder geben das zweite Argument nicht aus und geben das erste zurück:

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

Beachten Sie, dass der (Ergebnis der Auswertung des) tatsächlichen Operanden zurückgegeben wird, nicht der Wahrheitswert.

Die einzige Möglichkeit, ihr Verhalten anzupassen, besteht darin, es zu überschreiben __nonzero__( __bool__in Python 3 umbenannt), damit Sie beeinflussen können, welcher Operand zurückgegeben wird, aber nichts anderes zurückgeben. Listen (und andere Sammlungen) werden als "wahr" definiert, wenn sie überhaupt etwas enthalten, und als "falsch", wenn sie leer sind.

NumPy-Arrays lehnen diesen Gedanken ab: Für die Anwendungsfälle, auf die sie abzielen, sind zwei verschiedene Vorstellungen von Wahrheit üblich: (1) ob ein Element wahr ist und (2) ob alle Elemente wahr sind. Da diese beiden vollständig (und stillschweigend) inkompatibel sind und weder eindeutig korrekter noch häufiger vorkommen, weigert sich NumPy zu raten und verlangt, dass Sie explizit .any()oder verwenden .all().

&und |(und notübrigens) können vollständig außer Kraft gesetzt werden, da sie nicht kurzschließen. Sie können beim Überschreiben überhaupt alles zurückgeben, und NumPy nutzt dies gut, um elementweise Operationen durchzuführen, wie dies bei praktisch jeder anderen skalaren Operation der Fall ist. Listen hingegen senden keine Vorgänge über ihre Elemente hinweg. So wie mylist1 - mylist2es nichts bedeutet und mylist1 + mylist2etwas völlig anderes bedeutet, gibt es keinen &Operator für Listen.


3
Ein besonders interessantes Beispiel dafür, was dies hervorbringen kann, ist die [False] or [True]Bewertung von [False]und die [False] and [True]Bewertung von [True].
Rob Watts

16

Beispiel 1:

So funktioniert der Operator und .

x und y => wenn x falsch ist, dann x , sonst y

Mit anderen Worten, da dies mylist1nicht Falseder Fall ist , ist das Ergebnis des Ausdrucks mylist2. (Nur leere Listen werden ausgewertet False.)

Beispiel 2:

Der &Operator ist für eine bitweise und, wie Sie erwähnen. Bitweise Operationen funktionieren nur mit Zahlen. Das Ergebnis von a & b ist eine Zahl, die aus 1s in Bits besteht, die sowohl in a als auch in b 1 sind . Beispielsweise:

>>> 3 & 1
1

Mit einem binären Literal (gleiche Zahlen wie oben) ist es einfacher zu sehen, was passiert :

>>> 0b0011 & 0b0001
0b0001

Bitweise Operationen ähneln im Konzept booleschen (Wahrheits-) Operationen, funktionieren jedoch nur mit Bits.

Also, ein paar Aussagen über mein Auto gegeben

  1. Mein Auto ist rot
  2. Mein Auto hat Räder

Das logische "und" dieser beiden Aussagen lautet:

(Ist mein Auto rot?) und (Hat das Auto Räder?) => logisch wahr oder falsch

Beides gilt zumindest für mein Auto. Der Wert der Aussage als Ganzes ist also logisch wahr.

Das bitweise "und" dieser beiden Aussagen ist etwas nebulöser:

(der numerische Wert der Anweisung 'Mein Auto ist rot') & (der numerische Wert der Anweisung 'Mein Auto hat Räder') => Nummer

Wenn Python weiß, wie die Anweisungen in numerische Werte konvertiert werden, wird es dies tun und das bitweise und der beiden Werte berechnen. Dies mag dazu führen, dass Sie glauben, dass dies &austauschbar ist and, aber wie im obigen Beispiel handelt es sich um verschiedene Dinge. Für die Objekte, die nicht konvertiert werden können, erhalten Sie lediglich eine TypeError.

Beispiel 3 und 4:

Numpy implementiert arithmetische Operationen für Arrays:

Arithmetik- und Vergleichsoperationen auf ndarrays werden als elementweise Operationen definiert und liefern im Allgemeinen ndarray-Objekte als Ergebnisse.

Implementiert jedoch keine logischen Operationen für Arrays, da Sie logische Operatoren in Python nicht überladen können . Deshalb funktioniert Beispiel drei nicht, Beispiel vier jedoch.

Um Ihre andvs- &Frage zu beantworten : Verwenden Sie and.

Die bitweisen Operationen werden verwendet, um die Struktur einer Zahl zu untersuchen (welche Bits gesetzt sind, welche Bits nicht gesetzt sind). Diese Art von Informationen wird hauptsächlich in Betriebssystemschnittstellen auf niedriger Ebene verwendet ( z. B. Unix-Berechtigungsbits ). Die meisten Python-Programme müssen das nicht wissen.

Die logischen Operationen ( and, or, not), jedoch sind die ganze Zeit verwendet.


14
  1. In Python ein Ausdruck von X and YRückgaben Y, vorausgesetzt, dass bool(X) == Trueoder einer von Xoder Yzu False ausgewertet wird, z.

    True and 20 
    >>> 20
    
    False and 20
    >>> False
    
    20 and []
    >>> []
  2. Der bitweise Operator ist für Listen einfach nicht definiert. Es ist jedoch für Ganzzahlen definiert, die über die binäre Darstellung der Zahlen arbeiten. Betrachten Sie 16 (01000) und 31 (11111):

    16 & 31
    >>> 16
  3. NumPy ist keine psychische, weiß er nicht, ob Sie bedeuten , dass zB [False, False]sollte gleich Truein einem logischen Ausdruck. Dabei wird ein Standard-Python-Verhalten überschrieben, das lautet: "Jede leere Sammlung mit len(collection) == 0is False".

  4. Wahrscheinlich ein erwartetes Verhalten von NumPys Arrays & Operator.


Falsch und 20 kehrt zurück Falsch
Rahul

4

Für das erste Beispiel und basierend auf dem Django-Dokument
wird immer die zweite Liste zurückgegeben. In der Tat wird eine nicht leere Liste als True-Wert für Python angesehen. Python gibt also den 'letzten' True-Wert zurück, also die zweite Liste

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

Operationen mit einer Python-Liste arbeiten mit der Liste . list1 and list2prüft, ob list1es leer ist, und gibt zurück, list1ob es leer ist und list2ob es nicht ist. list1 + list2anhängen list2zu list1, so dass Sie eine neue Liste mit bekommen len(list1) + len(list2)Elementen.

Operatoren, die nur dann sinnvoll sind, wenn sie elementweise angewendet werden, z. B. &erhöhen Sie a TypeError, da elementweise Operationen nicht unterstützt werden, ohne die Elemente zu durchlaufen.

Numpy Arrays unterstützen elementweise Operationen. array1 & array2berechnet das bitweise oder für jedes entsprechende Element in array1und array2. array1 + array2berechnet die Summe für jedes entsprechende Element in array1und array2.

Dies funktioniert nicht für andund or.

array1 and array2 ist im Wesentlichen eine Abkürzung für den folgenden Code:

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

Dafür benötigen Sie eine gute Definition von bool(array1). Für globale Operationen, wie sie in Python-Listen verwendet werden, lautet die Definition, bool(list) == Truewenn sie listnicht leer sind und Falsewenn sie leer sind. Bei den elementweisen Operationen von numpy besteht eine gewisse Unklarheit darüber, ob geprüft werden soll, ob ein Element ausgewertet Truewird oder ob alle Elemente ausgewertet werden True. Da beide wohl korrekt sind, errät numpy nicht und löst ein ValueErrorWann aus, bool()das (indirekt) für ein Array aufgerufen wird.


0

Gute Frage. Ähnlich wie bei der Beobachtung, die Sie zu den Beispielen 1 und 4 (oder sollte ich 1 & 4 sagen :)) über logische andbitweise &Operatoren gemacht haben, habe ich Erfahrungen mit sumOperatoren gemacht. Numpy sumund Py sumverhalten sich ebenfalls unterschiedlich. Beispielsweise:

Angenommen, "mat" ist ein numpy 5x5 2d-Array wie:

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]])

Dann gibt numpy.sum (mat) die Gesamtsumme der gesamten Matrix an. Während die in Python integrierte Summe wie sum (mat) nur entlang der Achse summiert. Siehe unten:

np.sum(mat)  ## --> gives 325
sum(mat)     ## --> gives array([55, 60, 65, 70, 75])
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.