Les operateurs | et &: qu'est qu'ils peuvent bien vouloir dire...

Le problème exposé dans ce sujet a été résolu.

Bonjour tout le monde!

Je m’embrouille de plus en plus avec les operateur | et & en python. J’ai regarde pour les int, et j’ai vu que value1|value2 peut etre different de value2|value1. Pourtant

1
2
3
4
5
>>> 1|2
3
>>> 2|1
3
>>> 

Je sais que chaque objet a sa propre interpretation des operateur grace a methode magique, mais globalement, ca a generalement le meme sens, non? (la seule exception que j’ai trouve est / avec pathlib)

Donc, c’est ca ma question: que veulent dire les operateurs & et |.

Merci d’avance pour vos reponses!

Mathieu

Ce sont des opérateurs bit à bit.

& est le ’et’ binaire. (1 & 1 = 1 sinon = 0)

| est le ’ou’ binaire. (0 | 0 = 0 sinon 1 )

On prend la représentation des 2 nombres en binaire. Et on applique la règle précédante. Si le niem bit du premier nombre est 1 et le niem bit du deuxième nombre est 1 alors ’&’ donnera un niem bit de 1, sinon ce niem bit serra 0. Si le niem bit du premier nombre est 1 et le niem bit du deuxième nombre est 1 alors ’&’ donnera un niem bit de 1, sinon ce niem bit serra 0.

Si le niem bit du premier nombre est 0 et le niem bit du deuxième nombre est 0 alors ’|’ donnera un niem bit de 0, sinon ce niem bit serra 1.

Opérations bit à bit : https://fr.wikipedia.org/wiki/Opération_bit_à_bithttps://fr.wikipedia.org/wiki/Opération_bit_à_bit

La version anglaise est plus claire.

Bon courage !

+1 -0

Merci, j’avais un trou de mémoire (alzheimer, toussa toussa) et la flemme de chercher sur mon tél.

Édit: et si tu veux un autre exemple de détournement des opérateurs math, il y a aussi % sur les chaînes de caractères.

+0 -0

Au sens purement mathématique (dans le sens courant hein, parce que bien sûr on peut définir ce qu’on veut en maths) == n’est pas une opération mais une relation (c’est pour ca qu’on la qualifie de symétrique et pas de commutative). Au sens programmatique, par contre, oui, == est un opérateur commutatif.

+2 -0
Banni

J’ai regarde pour les int, et j’ai vu que value1|value2 peut etre different de value2|value1.

math2001

Et où as-tu vu ça ?

entwanne

Peut-être que math2001 parlait du "court-circuit" ? Mais c’est or qui court-circuite en python, ce que ne fait pas |.

1
2
3
4
5
6
7
8
9
def test(str,b):
  print(str)
  return b

test("A", True) | test("B", True)
  # affiche "A" et "B"
test("A", True) or test("B", True)
  # affiche uniquement "A" et retourne directement True
  # puisque ça ne dépend pas de la valeur de test("B", True)

Il n’y a que l’ordre dans lequel sont évalués A et B qui change entre A | B et B | A.

Au sens purement mathématique (dans le sens courant hein, parce que bien sûr on peut définir ce qu’on veut en maths) == n’est pas une opération mais une relation (c’est pour ca qu’on la qualifie de symétrique et pas de commutative). Au sens programmatique, par contre, oui, == est un opérateur commutatif.

En general on definit la commutativite pour des fonctions $f : X \times X \to Y$ (ca veut dire $f(x, x') = f(x', x)$ pour tout $(x, x') \in X \times X$), donc ca a bien un sens "naturel" de dire == est commutatif, en voyant une relation comme une application d’un produit cartesien dans $\{0, 1\}$ par exemple (auquel cas il faut remarquer que (x == y) = (y == x), avec le = du milieu qui est le = mathematique… (x == y) == (y == x) exprime autre chose).

Dans ces circonstances il est equivalent de dire == est commutatif ou == est symetrique, mais bon, c’est du pinaillage pas tres essentiel, ca montre juste a quel point les deux notions sont proches. (Ca rentre en quelque sorte dans le "on peut definir ce qu’on veut en maths", mais en partant de ce constat la remarque de nohar n’est pas forcement justifiee non plus, donc bon… :p)

+0 -1

Un truc intéressant à noter à propos de | et de &, qui à ma connaissance n’est possible qu’en python, est qu’ils peuvent être utilisés sur des ensembles, également avec le sens de "et" (intersection) et de "ou" (union). C’est un comportement plutôt intuitif et sympathique je trouve.

1
2
3
4
>>> {1,2,3,4} & {3,4,5,6}
{3,4}
>>> {1,2,3,4} | {3,4,5,6}
{1,2,3,4,5,6}

Bizarrement, il n’y a même pas de méthodes correspondantes dans la classe set. Je m’attendais à en trouver.

+2 -0

Il n’y a que l’ordre dans lequel sont évalués A et B qui change entre A | B et B | A.

blo yhg

Ce qui peut, bien sûr, changer le résultat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Dummy:
    def __init__(self, reftob):
        self.reftob = reftob

    def __getitem__(self, itm):
        self.reftob['val'] += 1
        return itm

b = dict(val=3)
a = Dummy(b)

print(a[5] | b['val'])  # 5
# print(b['val'] | a[5])  # 7

Bienvenue dans le monde des variables mutables. L’exemple peut paraître tiré par les cheveux, mais c’est le genre de problématique qui fait qu’il est impossible (pour un compilateur par exemple) de factoriser f(x) + f(x) en 2 * f(x) si on autorise f à avoir des effets de bords.

EDIT : je viens de réaliser que ça a une conséquence assez lourde en fait. Une application quelconque a beau être symétrique après évaluation de ses arguments, l’évaluation elle-même n’étant pas symétrique, il est impossible de garantir la symétrie de l’application.

+0 -0

Desole, j’ai pas eu le temps de poster mon message hier:

J’ai capte! Merci @ache et @tleb! Mais alors j’en vois pas l’ombre d’une utilite… Euh, comment on fait pour choper la representation en bit? J’ai vu des trucs comme ca 1 << 6, but it confuses me even more.

@etwanee et @blo yhg: ben maintenant, c’est ca qui me pose probleme. J’ai fais un coup de vieux help(int), et en regardant les functions methodes, je suis tombe la dessus:

1
2
3
4
5
6
7
|  __and__(self, value, /)
|      Return self&value.

[...]

|  __rand__(self, value, /)
|      Return value&self.

Et meme chose pour le |. J’ai donc conclu qu’il y avait une difference, mais maintenant je la comprends pas… :(

J’ai fais plusieurs tests, mais c’est tout le temps la meme chose:

 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
26
>>> both = lambda a, b: print(a & b, b & a, (b & a) == (a & b))
>>> from random import randint as rnd
>>> for i in range(20):
...     both(rnd(0, 1000), rnd(0, 1000))
...
2 2 True
2 2 True
20 20 True
90 90 True
44 44 True
656 656 True
65 65 True
570 570 True
129 129 True
2 2 True
192 192 True
157 157 True
320 320 True
210 210 True
256 256 True
1 1 True
336 336 True
3 3 True
108 108 True
26 26 True
>>>

Édit: et si tu veux un autre exemple de détournement des opérateurs math, il y a aussi % sur les chaînes de caractères.

Kje

Ah oui, je l’avais oublie c’ui la… Je regrette que % n’appelle pas .format, c’est plus rapide, et je trouve ca plus style. :D

@adri1: j’ai du mal a comprendre ton exemple. Les resultats sont differents juste parce que tu change les valeurs… Le | est toujours symetrique

@QuentinC Je connaissais pas ca, merci! Mais c’est un comportement different ca, non? C’est pas a cause des bits non? (en relisant, on dirai que je dis que tu n’aurais pas du poster ton message, il est hors-sujet. Je tiens a preciser que ce n’est pas le cas :) )

Bon, au final, j’arrive maintenant avec deux questions:

  • pourquoi les int on deux differentes methodes puisqu’elles font la meme choses? Peut-etre c’est juste pour permettre a l’utilisateur de le faire dans n’importe quel ordre. Si c’est le cas, alors j’ai manque un truc sur les __magic__, il va falloir que j’y re-reflechisse.
  • quelle est l’utilite de ces operations sur les bits?

Merci pour vos nombreuses reponses!

Et au passage, joyeux noel!

Mathieu

__rand__ et __and__ sont implémentées de façon à permettre un fallback à l’évaluation de a & b sur b.__rand__(a) si a.__and__(b) n’est pas implémentée.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Dummy:
    def __and__(self, value):
        return 'left and'

    def __rand__(self, value):
        return 'right and'

a = Dummy()

print(a & None)
print(None & a)

@adri1: j’ai du mal a comprendre ton exemple. Les resultats sont differents juste parce que tu change les valeurs… Le | est toujours symetrique…

Ben tu vois bien que non. a[5] | b['val'] ne donne pas le même résultat que b['val'] | a[5]. De manière générale, tu n’as aucune garantie que expr1 | expr2 va donner le même résultat que expr2 | expr1 puisque l’évaluation de expr1 peut affecter celle de expr2 et inversement. | (ou n’importe quelle autre application qui devrait l’être) n’est symétrique que vis-à-vis de littéraux, pas dans le cas général.

+1 -0

Il me semble toutefois que cette surcharge est également possible en C++.

Oui, on peut certaiement écrire une fonction template<class T> std::set<T> operator& (const std::set<T>&, const std::set<T>&) , mais elle n’est pas disponible nativement.

Je connaissais pas ca, merci! Mais c’est un comportement different ca, non?

C’est différent, oui et non. Oui parce que c’est des opérations sur d’autres types. Mais au fond pour les nombres tu pourrais imaginer que tu travaille sur des ensembles de bits, un à un; ça pourrait t’aider à comprendre l’analogie, et voir qu’au final c’est pas si différent que ça.

Pour compléter le tableau, les opérations - et ^sont aussi disponibles pour les ensembles et calculent respectivement la différence et la différence symétrique. On peut voir aussi assez facilement l’analogie entre la différence symétrique et le ou exclusif.

1
2
3
4
>>> {1,2,3,4,5} - {4,5,6,7}
{1,2,3}
>>> {1,2,3,4,5} ^{2,3,4,5,6}
{1,6}

quelle est l’utilite de ces operations sur les bits?

Dans les utilisations classiques informatiques il y a le concept de champ de bits ou flags. Théoriquement avec des langages très haut niveau comme python on ne devrait plus le faire parce qu’il y a mieux et plus lisible, mais ça sert quand même toujours.

Mathématiquement, ça permet de faire des calculs plus rapides quand on travaille avec des puissances de 2. C’est plus rapide de faire des opérations sur les bits directement, plutôt que des multiplications et difisions. Par exemple, x<<n équivaut à x*(2**n)et inversément, x>>n à x/(2**n). x%(2**n) peut être remplacé par x&(2**n-1), et 2**nlui-même peut être remplacé par 1<<n. Enfin, x&~(2**n-1) permet de tronquer un nombre.

+1 -0

@adri1: Ok pour le fallback, c’est plus clair maintenant. A propos de ton example, j’avais capte, ce que je voulais dire c’etait que le resultat change parce que les valeurs finales changent, pas parce que l’ordre dans lequel elles sont. Desole, c’est qu’est pas ete tres fute sur ce coup…

@quentinC: Merci pour ces exemples sur les set. Donc, l’operateur & (et |) sont base sur le meme principe pour les bit et les set, mais pas le meme systems. Ok pour l’utilisation des bits, je ne pense avoir a m’en servir rapidement… :D

Merci encore pour vos reponses!

Mathieu

A propos de ton example, j’avais capte, ce que je voulais dire c’etait que le resultat change parce que les valeurs finales changent, pas parce que l’ordre dans lequel elles sont.

Mais les valeurs finales changent justement à cause de l’ordre dans lequel elles sont. C’est pour ça que dire que | ou + ou même == sont commutatifs, c’est aller un peu vite en besogne. Ce ne serait le cas que si l’ordre d’évaluation des expressions ne dépendait pas de leur position autour de l’opérateur, ou que l’ordre d’évaluation des expressions n’affectait pas leur valeur. La seule chose que l’on puisse dire, c’est que ces opérateurs sont commutatifs vis-à-vis de littéraux. Ce qui serait beaucoup plus intéressant serait la commutativité vis-à-vis d’expressions quelconques (donc la vraie commutativité au sens mathématique), ce qui est impossible en Python.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte