Différence entre ' et '(booléen) vs. ' & '(bit à bit) en python. Pourquoi la différence de comportement avec les listes vs les tableaux numpy?

Ce qui explique la différence de comportement des opérations booléennes et binaires sur les listes vs numpy.les tableaux?

Je suis confus au sujet de l'utilisation appropriée du '&' vs 'and' en python, illustré dans les exemples simples suivants.

    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! 

Ce réponse, ce réponse les deux m'ont aidé à comprendre que " et " est une opération booléenne mais '&' est une opération au niveau du bit.

J'étais en train de lire certains informations pour mieux comprendre le concept de opérations binaires , mais j'ai du mal à utiliser cette information pour donner un sens à mes exemples 4 ci-dessus.

Notez, dans ma situation particulière, ma sortie désirée est une newlist où:

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

L'exemple 4, ci-dessus, m'a conduit à ma sortie désirée, donc c'est bien.

Mais je me sens confus quant à quand / comment / pourquoi je devrais utiliser ' et 'vs'&'. Pourquoi les listes et les tableaux numpy se comportent-ils différemment avec ces opérateurs?

Quelqu'un peut-il m'aider à comprendre la différence entre les opérations booléennes et binaires pour expliquer pourquoi ils gèrent les listes et numpy.tableaux différemment?

Je veux juste m'assurer que je continue à utiliser ces opérations correctement à l'avenir. Merci beaucoup pour l'aide!

Numpy version 1.7.1

python 2.7

References all inline with text.

MODIFICATIONS

1) Merci @delnan d'avoir souligné que dans mes exemples originaux, j'avais une ambiguïté qui masquait ma confusion plus profonde. J'ai mis à jour mes exemples pour clarifier à ma question.

90
demandé sur Community 2014-03-26 01:18:23

7 réponses

and teste si les deux expressions sont logiquement True tout & (lorsqu'il est utilisé avec True/False les valeurs) teste si les deux sont True.

En Python, les objets intégrés vides sont généralement traités comme logiquement False tandis que les éléments intégrés non vides sont logiquement True. Cela facilite le cas d'utilisation courant où vous voulez faire quelque chose si une liste est vide et autre chose si la liste ne l'est pas. Notez que cela signifie que la liste [False] est logiquement True:

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

Donc dans L'exemple 1, la première liste n'est pas vide et donc logiquement True, donc la valeur de vérité de la and est la même que celle de la deuxième liste. (Dans notre cas, la deuxième liste n'est pas vide et donc logiquement True, mais l'identification qui nécessiterait une étape de calcul inutile.)

Par exemple 2, les listes ne peuvent pas être combinées de manière significative au niveau du BIT car elles peuvent contenir des éléments arbitraires différents. Les choses qui peuvent être combinées au niveau du BIT incluent: Trues et Falses, entier.

Les objets NumPy, en revanche, prennent en charge les calculs vectorisés. C'est qu'ils vous permettent d'effectuer les mêmes opérations sur plusieurs éléments de données.

L'exemple 3 échoue car les tableaux NumPy (de longueur > 1) n'ont pas de valeur de vérité car cela empêche la confusion logique basée sur des vecteurs.

L'exemple 4 est simplement une opération de bit vectorisé and.

Résultat Net

  • Si vous n'avez pas affaire à des tableaux et que vous n'effectuez pas de calculs manipulations d'entiers, vous voulez probablement and.

  • Si vous avez des vecteurs de valeurs de vérité que vous souhaitez les combiner, utiliser numpy avec &.

74
répondu ramcdougal 2014-03-25 21:47:12

Les opérateurs booléens de court-circuit(and, or) ne peut pas être remplacé car il n'y a pas de moyen satisfaisant de le faire sans introduire de nouvelles fonctionnalités de langage ou sacrifier le court-circuit. Comme vous le savez peut-être ou non, ils évaluent le premier opérande pour sa valeur de vérité, et en fonction de cette valeur, évaluez et renvoyez le deuxième argument, ou n'évaluez pas le deuxième argument et renvoyez le PREMIER:

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

Notez que le (résultat de l'évaluation de l'opérande) réel est retourné, pas la valeur de vérité de celui-ci.

La seule façon de personnaliser leur comportement est de remplacer __nonzero__ (renommé en __bool__ dans Python 3), de sorte que vous pouvez affecter quel opérande est retourné, mais ne pas renvoyer quelque chose de différent. Les listes (et autres collections) sont définies comme étant "véridiques" lorsqu'elles contiennent quoi que ce soit, et "falsey" lorsqu'elles sont vides.

Les tableaux NumPy rejettent cette notion: pour les cas d'utilisation qu'ils visent, deux notions différentes de vérité sont communes: (1) si un élément est vrai, et (2) si tous les éléments sont vrais. Puisque ces deux sont complètement (et silencieusement) incompatibles, et que ni l'un ni l'autre n'est clairement plus correct ou plus commun, NumPy refuse de deviner et vous oblige à utiliser explicitement .any() ou .all().

& et | (et not, en passant) peut être complètement remplacé, car ils ne court-circuitent pas. Ils peuvent renvoyer n'importe quoi lorsqu'ils sont surchargés, et NumPy en fait bon usage pour effectuer des opérations par élément, comme ils le font avec pratiquement n'importe quel autre scalaire de l'opération. Les listes, d'autre part, ne diffusent pas d'opérations sur leurs éléments. Tout comme mylist1 - mylist2 ne signifie rien et mylist1 + mylist2 signifie quelque chose de complètement différent, il n'y a pas d'opérateur & pour les listes.

19
répondu 2014-03-25 21:49:54

À propos de list

D'abord un point très important, dont tout va suivre (j'espère).

En Python ordinaire, list n'est en aucun cas spécial (sauf avoir une syntaxe mignonne pour la construction, qui est principalement un accident historique). Une fois qu'une liste [3,2,6] est créée, il s'agit à toutes fins utiles d'un objet Python ordinaire, comme un number 3, set {3,7} ou une function lambda x: x+5.

(oui, il prend en charge la modification de ses éléments, et il prend en charge l'itération, et beaucoup d'autres choses, mais c'est juste ce qu'est un type: il prend en charge certaines opérations, tout en ne supportant pas d'autres. int supporte l'élévation à une puissance, mais cela ne le rend pas très spécial - c'est juste ce qu'est un int. lambda supporte les appels, mais cela ne le rend pas très spécial-c'est ce que lambda est pour, après tout:).

À propos de and

and n'est pas un opérateur (vous pouvez l'appeler "opérateur", mais vous pouvez l'appeler "pour" un opérateur de trop:). Les opérateurs en Python sont (implémentés via) les méthodes appelées sur des objets d'un certain type, généralement écrites dans le cadre de ce type. Il n'existe aucun moyen d'une méthode pour contenir une évaluation de certains de ses opérandes, mais and peut (et doit) faire.

, ce qui fait que and ne peut pas être surchargé, tout comme for ne peut pas être surchargé. Il est complètement général et communique via un protocole spécifié. Ce que vous peut faire est de personnaliser votre partie du protocole, mais cela ne signifie pas que vous pouvez modifier le comportement de and complètement. Le protocole est:

Imaginez Python interprétant " a et b "(cela n'arrive pas littéralement de cette façon, mais cela aide à comprendre). Quand il s'agit de "et", il ressemble à l'objet qu'il vient d'évaluation (a), et lui demande: êtes-vous vrai? (PAS: êtes-vous True?) Si vous êtes un auteur, d'une classe, vous pouvez personnaliser cette réponse. If a répond "non", and (saute b complètement, elle n'est pas évaluée à tous, et) dit: a est mon résultat (PAS: Faux est mon résultat).

Si {[13] }ne répond pas, and lui demande: Quelle est votre longueur? (Encore une fois, vous pouvez personnaliser cela en tant qu'auteur de la classe de a). Si a répond à 0, and fait la même chose que ci - dessus-le considère faux (pas faux), ignore b et donne a comme résultat.

Si a répond à autre chose que 0 à la deuxième question ("Quelle est votre longueur"), ou qu'il ne répond pas du tout, ou qu'il répond "oui" à la première ("êtes-vous Vrai"), and évalue b et dit: b est mon résultat. Notez qu'il nepas poser des questions b.

L'autre manière de dire c'est que la a and b est presque le même que b if a else a, sauf un est évaluée qu'une seule fois.

Maintenant, asseyez - vous quelques minutes avec un stylo et du papier, et convainquez-vous que lorsque {A,b} est un sous-ensemble de {True,False}, cela fonctionne exactement comme on peut s'y attendre des opérateurs booléens. Mais j'espère que je vous ai convaincu qu'il est beaucoup plus général, et comme vous le verrez, beaucoup plus utile cette façon.

Mettre ces deux ensemble

Maintenant, j'espère que vous comprenez votre exemple 1. and ne se soucie pas si mylist1 est un nombre, une liste, lambda ou un objet D'une classe Argmhbl. Il se soucie juste de la réponse de mylist1 aux questions du protocole. Et bien sûr, mylist1 répond 5 à la question sur la longueur, so et renvoie mylist2. Et c'est tout. Cela n'a rien à voir avec les éléments de mylist1 et mylist2 - ils n'entrent dans l'image nulle part.

Deuxième exemple: & sur list

D'autre part, & est un opérateur comme les autres, comme +, par exemple. Il peut être défini pour un type en définissant une méthode spéciale sur cette classe. int le définit comme " et "au niveau du BIT, et bool le définit comme" et " logique, mais ce n'est qu'une option: par exemple, les sets et d'autres objets comme les vues dict keys le définissent comme une intersection set. list ne le définit tout simplement pas, probablement parce que Guido n'a pas pensé à une façon évidente de définir il.

Numpy

Sur l'autre jambe: - D, les tableaux numpy sont spéciaux, ou du moins ils essaient de l'être. Bien sûr, numpy.array est juste une classe, il ne peut pas remplacer and de quelque façon que ce soit, donc il fait la meilleure chose: lorsqu'on lui demande "Êtes-vous vrai", numpy.array soulève une ValueError, en disant Effectivement "veuillez reformuler la question, ma vision de la vérité ne rentre pas dans votre modèle". (Notez que le message ValueError ne parle pas de and - parce que numpy.tableau ne sais pas qui lui pose la question; il parle juste de la vérité.)

Pour &, c'est une histoire complètement différente. numpy.array peut le Définir comme il le souhaite, et il définit & de manière cohérente avec les autres opérateurs: pointwise. Donc, vous obtenez enfin ce que vous voulez.

HTH,

14
répondu Veky 2014-03-26 06:50:25

Exemple 1:

Voici comment fonctionne l'opérateur et.

x et y => if x est faux, alors x, else y

Donc, en d'autres termes, puisque mylist1 n'est pas False, le résultat de l'expression est mylist2. (Seulement listes vides évaluer à False.)

Exemple 2:

L'opérateur & est pour un bit et, comme vous le mentionnez. Les opérations binaires ne fonctionnent que sur nombre. Le résultat de un & b est un nombre composé de 1s dans les bits qui sont à 1 dans les deux un et b. Par exemple:

>>> 3 & 1
1

Il est plus facile de voir ce qui se passe en utilisant un littéral binaire (mêmes nombres que ci-dessus):

>>> 0b0011 & 0b0001
0b0001

Les opérations au niveau du BIT sont similaires dans le concept aux opérations booléennes (vérité), mais elles ne fonctionnent que sur les bits.

Donc, étant donné quelques déclarations sur ma voiture

  1. Ma voiture est rouge
  2. mon la voiture a des roues

Le " et " logique de ces deux énoncés est:

(ma voiture est rouge?) et (n'voitures ont des roues?) = > logique VRAI de la valeur fausse

Les deux sont vraies, pour ma voiture au moins. Ainsi, la valeur de la déclaration dans son ensemble est logiquement vrai.

Le bitwise "et" de ces deux déclarations est un peu plus nébuleux:

(la valeur numérique de la déclaration de 'ma voiture est rouge') & (la valeur numérique de la déclaration 'ma voiture a des roues') = > nombre

Si python sait comment convertir les instructions en valeurs numériques, il le fera et calculera le bit-et des deux valeurs. Cela peut vous amener à croire que & est interchangeable avec and, mais comme avec l'exemple ci-dessus ce sont des choses différentes. En outre, pour les objets qui ne peuvent pas être convertis, vous obtiendrez simplement un TypeError.

Exemple 3 et 4:

Numpy implémente arithmétique des opérations pour les tableaux:

Les opérations arithmétiques et de comparaison sur ndarrays sont définies comme des opérations par élément, et donnent généralement des objets ndarray comme résultats.

Mais n'implémente pas d'opérations logiques pour les tableaux, car vous ne pouvez pas surcharger les opérateurs logiques en python. C'est pourquoi l'exemple trois ne fonctionne pas, mais l'exemple quatre le fait.

Donc, pour répondre à votre and vs & question: Utilisez and.

Les opérations binaires sont utilisés pour examiner la structure d'un nombre (quels bits sont définis, quels bits ne sont pas définis). Ce type d'information est principalement utilisé dans les interfaces de système d'exploitation de bas niveau (bits d'autorisation unix, par exemple). La plupart des programmes python n'auront pas besoin de le savoir.

Les opérations logiques (and, or, not), toutefois, sont utilisés tout le temps.

11
répondu Seth 2017-05-23 11:33:25
  1. En Python une expression de X and Y retourne Y, étant donné que bool(X) == True ou l'une de X ou Y False, par exemple:

    True and 20 
    >>> 20
    
    False and 20
    >>> False
    
    20 and []
    >>> []
    
  2. L'opérateur bit À bit n'est tout simplement pas défini pour les listes. Mais il est défini pour les entiers-opérant sur la représentation binaire des nombres. Considérons 16 (01000) et 31 (11111):

    16 & 31
    >>> 16
    
  3. NumPy n'est pas un psychique, il ne sait pas, si vous voulez dire que par exemple, [False, False] doit être égal à True dans une logique expression. En cela, il remplace un comportement Python standard, qui est: "toute collection vide avec len(collection) == 0 est False".

  4. Probablement un comportement attendu de l'opérateur & des tableaux de NumPy.

10
répondu Zaur Nasibov 2017-08-22 11:06:29

Les opérations avec une liste Python fonctionnent sur la liste . list1 and list2 va vérifier si list1 est vide, et le retour list1 si elle est, et list2, si elle n'est pas. list1 + list2 ajouter list2 à list1, de sorte que vous obtenez une nouvelle liste avec len(list1) + len(list2) éléments.

Les opérateurs qui n'ont de sens que lorsqu'ils sont appliqués au niveau de l'élément, tels que &, déclenchent un TypeError, car les opérations au niveau de l'élément ne sont pas prises en charge sans boucler les éléments.

Les tableaux Numpy prennent en charge les opérations par élément. array1 & array2 calculera le bit ou pour chaque élément correspondant dans array1 et array2. array1 + array2 calcule la somme pour chaque élément correspondant dans array1 et array2.

, Cela ne fonctionne pas pour and et or.

array1 and array2 est essentiellement une main courte pour le code suivant:

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

Pour cela, vous avez besoin d'une bonne définition de bool(array1). Pour les opérations globales comme celles utilisées sur les listes Python, la définition est la suivante: bool(list) == True si list n'est pas vide, et False s'il est vide. Pour les opérations relatives aux éléments de numpy, il y a une certaine ambiguïté quant à savoir si un élément évalue à True, ou si tous les éléments évaluent à True. Parce que les deux sont sans doute corrects, numpy ne devine pas et soulève un ValueError quand bool() est (indirectement) appelé sur un tableau.

4
répondu knbk 2014-03-26 06:57:13

Pour le premier exemple et la base sur le doc de django
Il retournera toujours la deuxième liste, en effet une liste non vide est vue comme une vraie valeur pour Python ainsi python retourne la 'dernière' valeur vraie donc la deuxième 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]
3
répondu MoiTux 2014-03-25 22:40:01