Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Petites questions théoriques : peut-on comparer une variable liste avec un pointeur en C?

26 réponses
Avatar
Francois
Bonjour à tous,

J'ai quelques questions qui peut-être auront un rapport avec
l'implémentation, mais cela m'intéresse quand même.


1) Quand on fait

a = 2
b = a

Les deux variables a et b sont deux variables différentes, la zone
mémoire allouée pour a et celle pour b sont distinctes. Une modification
de a n'affectera pas b etc. Chacun vit ça vie.

Ce qui me chagrine, c'est que id(a) et id(b) me donne exactement la même
valeur, alors que, pour moi, a et b sont distinctes. D'ailleurs quand je
modifie b (via b = 50 par exemple), là, ce petit coquin de Python ne me
donne plus le même id (et heureusement). Avez vous une explication ?


2) Pour les listes, c'est différent et c'est bien dit dans les docs. Par
exemple :

l1 = [0,1,2,3,4]
l2 = l1

J'ai bien compris que "le nom l2 est un alias du nom l1" qui désigne le
même objet dans la mémoire de l'ordinateur. Une modification via l1 se
retrouvera en appelant la liste via l2. Par exemple :

l1[0] = 100

entraînera l2 de la forme [100,1,2,3,4]

Je voulais trouver une petite explication à cela. J'aimerais avoir votre
avis. Je me suis dit que, peut-être, l1 et l2 étaient des sortes de
pointeurs (comme en C) sur le premier élément de la liste.


3) J'ai lu ceci "avec a = 3, a est une référence, c'est-à-dire un nom
désignant l'emplacement mémoire d'une valeur (objet)". Cela veut-il dire
qu'en fait les variables dans Python sont toutes "des pointeurs en C" ?



--
François

10 réponses

1 2 3
Avatar
Francois
Bon, là j'exagère un peu. b[0]=7 est (je pense) une assignation du
premier élément de l'objet séquence référencé par a (ou par b),


Dans la cas d'une liste, oui, on en arrive finalement à ça. Mais je
pourrais écrire une classe telle que la même syntaxe n'effectue aucune
assignation.


Je n'en doute pas. :-)


Cette réassignation, de fait, entraîne une modification de
l'objet séquence référencé par b et aussi par a.


Tout à fait. Mais ne modifie pas l'espace de nommage en cours, et
n'affecte pas les liaisons entre a et b et l'objet séquence (précision
à l'adresse de l'OP).


Heu, OP, ça veut dire quoi ?


Merci encore pour toutes ces explications, fichtrement intéressantes.


--
François


Avatar
Bruno Desthuilliers
On 9 avr, 01:12, Francois wrote:
(snip)



Par exemple avec ceci (en Python)

a=1
b=1
c=1

finalement, dans la mémoire se trouve un seul objet créé (l'entier 1).


Avec CPython, oui, mais parce que les petits entiers sont conservé en
cache.


Heu, c'est quoi le cache ? Pour moi, c'est une sorte de mémoire RAM qui
est seulement plus rapide et moins volumineuse que la RAM classique.
Est-ce juste ?


Oui, mais sans aucun rapport - là tu parles du cache processeur !-)

Quand je dis que les petits entiers sont conservés en cache, je veux
dire que la machine virtuelle réserve une zone mémoire pour les stocker
au fur et à mesure de leur création, et quand le programme demande un
objet entier avec une valeur correspondante, c'est l'objet déjà créé qui
est retourné - ce qui évite d'en créer un nouveau. Comme ces objets sont
immutables, qu'on se contrefout de leur identité (on ne s'intéresse qu'à
leur valeur), et qu'un objet Python prend *beaucoup* plus de place (et
coûte beaucoup plus cher à créer) qu'un entier C, c'est une optimisation
évidente.

Si oui, en quoi cela justifie que 1 se trouve en un seul endroit de la
mémoire ?

Si tu remplace 1 par 33333, tu aura trois objets différents.


Et zut. Ça me chagrine un peu ça. En effet :

a = 333333
b = 333333
id(a)
135973460



id(b)
135973424




et donc la comparaison "id(a) == id(b)"


Qui s'exprime habituellement par l'opérateur d'identité 'is':

a is b <=> id(a) == id(b)

n'est pas logiquement
équivalente à la comparaison "a==b". C'est dommage, non ?


C'est le problème de la différence entre l'égalité (de valeur) et
l'identité. Selon les types, les deux peuvent être équivalents ou non.

Dis-toi bien que la grande majorité des objets sont 1/ mutables, et 2/
avec un état beaucoup plus complexe qu'une simple valeur. La notion
d'égalité de valeur est arbitraire (en ce qu'elle est définie par le
type), celle d'identité ne l'est pas (deux objets sont identiques si
c'est le même objet, point, barre).

Je pensais que si Python tombait sur une affectation d'un objet déjà
existant, il ne le récréait pas, mais faisait une référence à celui déjà
existant, cela dans le but de prendre moins de place dans la mémoire.


a = [1, 2, 3]
b = [1, 2, 3]

A ce stade a et b sont égaux. Mais c'est accidentel. Doivent-ils être
identiques ? je peux vouloir modifier b sans modifier a

b[0] = 42

Si a et b avaient été identique, j'aurais, avec cette instruction,
modifié a également. Ainsi que toutes les autres listes de valeur
[1,2,3] existentes. Pas forcément ce que je voulais...

Pourquoi diable adopter cette stratégie quand on a des «petits» entiers,
et ne pas l'adopter pour des «gros» entiers ??? Je ne saisis pas bien la
logique là dedans ?


Eviter de charger inutilement le cache. Les petits entiers sont
extrêmement utilisés, les gros nettemement moins. Accessoirement, la
'limite' à partir de laquelle les entiers ne sont plus cachés a évolué
récemment - en partie suite à l'évolution du hard. Tout ça est un détail
d'implémentation, point barre.


Par contre, si tu fais:

a = 33333
b = a
c = b

Tu aura bien trois noms référençant un seul et même objet.


Ouf !


Remarque en passant : Je me doutais bien que d'un langage à un autre,
des mêmes notions pouvaient avoir une implémentation différente. Mais
quand même, je n'imaginais pas que cela serait le cas pour la basique
notion de variable par exemple !


Attends de voir un langage fonctionnel pur (comme Haskell par
exemple) : tu ne peux tout simplement pas modifier la valeur d'une
variable une fois qu'elle est crée !-)


On laissera ça pour ... une autre vie :-)


Pourquoi ? C'est intéressant aussi, comme approche, et il y a beaucoup à
en apprendre. Un des principes de la programmation fonctionnelle est
qu'une fonction ne doit pas avoir d'effets de bords (modifications de
l'état non-local) et que sa valeur (ce qu'elle retourne) ne doit
dépendre que et uniquement que de ses entrées (les paramètres qui lui
sont passés). Ce n'est pas toujours le plus efficace du point de vue des
ressources, mais ça aide (entre autres) à écrire des programmes faciles
à comprendre et maintenir. Bien qu'avant tout un langage objet, Python a
pas mal emprunté aussi à la programmation fonctionnelle, et tu verra pas
mal d'idiomes fonctionnels en Python.

J'espère que d'un langage à un autre,
il ne faut pas tout réapprendre à chaque fois (car là, finalement, j'ai
du réapprendre ce qu'est une variable :-) ) ?


Non, la sémantique des variables de Python (de ce point de vue là) se
retrouve, à l'identique ou de façon très proche, dans pas mal d'autres
langages de haut niveau. De même que la sémantique des variables C se
retrouve dans pas mal de langages de bas niveau.


Ouf !


Ok, ok. Allez, quand même, en interne de chez interne (dans le code C de
CPython par exemple), il n'y a pas de pointeur qui traîne quelque part
dans la définition d'une variable Python ? Hein, pour me faire
plaisir. :-)


Si, bien sûr. cf ci-dessus pour l'implémentation du type objet dans
CPython. A vrai dire, y a même tellement de pointeurs que tu ne sais
plus où donner de la tête. Content ?-)


Oui ! :-)


En Python, tu n'a *que* des références sur des objets, *jamais* de
types 'immédiat' comme en C. Ne laisse pas la syntaxe te tromper, ce
n'est pas parce que tu définis un objet via une expression littérale
que tu a autre chose qu'une référence sur un objet.
Heu, un type immédiat c'est quoi ?



un entier en C.


Mais s'il y a *que* des références à des objets, cela veut dire que le
simple entier "1" en Python est un réalité un objet plus complexe qu'un
simple int ou char en C. Son codage binaire par exemple sera plus
complexe que le naturel "00000001" ?


C'est rien de le dire. A ton avis, pourquoi on les mets en cache ?

Tiens, si tu veux en savoir plus, essaie ça dans ton shell Python:

a = 1
for name in dir(a):
print name, " : ", getattr(a, name)

Et encore, les entiers (et quelques autres types 'builtin') ont une
implémentation spécifique pour raisons d'optimisation.

Tu peux aussi, bien sûr, aller lire le code source de CPython (c'est un
logiciel libre, n'est-ce pas...)




Avatar
Bruno Desthuilliers
(snip)
T'inquiètes pas, t'es pas le premier (et c'est pas non plus la
première fois que j'explique ça).


Ah, désolé. Faut dire, à ma décharge, que tout cela n'est pas vraiment
bien expliqué dans le peu de livres que j'ai.


Faudrait p'tet que j'en écrive un alors !-)

Plus sérieusement: vu la diversité de parcours de débutants en Python,
écrire un texte qui convienne à tous est une gageure.


Avatar
Francois
C'est le problème de la différence entre l'égalité (de valeur) et
l'identité. Selon les types, les deux peuvent être équivalents ou non.

Dis-toi bien que la grande majorité des objets sont 1/ mutables, et 2/
avec un état beaucoup plus complexe qu'une simple valeur. La notion
d'égalité de valeur est arbitraire (en ce qu'elle est définie par le
type), celle d'identité ne l'est pas (deux objets sont identiques si
c'est le même objet, point, barre).

[couic]

a = [1, 2, 3]
b = [1, 2, 3]

A ce stade a et b sont égaux. Mais c'est accidentel. Doivent-ils être
identiques ? je peux vouloir modifier b sans modifier a

b[0] = 42

Si a et b avaient été identique, j'aurais, avec cette instruction,
modifié a également. Ainsi que toutes les autres listes de valeur
[1,2,3] existentes. Pas forcément ce que je voulais...


Ok. J'ai l'impression qu'en gros, il faut retenir ceci :

Avec

a = <truc>
b = <truc>

(où <truc> est un objet) dans la grande majorité des cas (pour la
plupart des objets <truc>), a et b se réfèrent à des objets distincts.
C'est par exemple toujours le cas quand <truc> est un objet mutable.
Quand <truc> est un objet entier pas trop gros, où finalement seul sa
valeur nous intéresse et qui n'est pas mutable, alors dans ce cas
particulier a et b se réfèrent exactement au même objet qui se trouvera
dans le «cache». Mais, finalement, ce cas particulier est un *détail*.
Les raisons de ce détail, tu les as dites : un entier est non mutable
donc on se moque éperdument de son identité, autant prendre la même
quand l'entier est déjà créé avant. Quand l'entier est trop «gros», on
retombe dans le cas général pour ne pas encombrer le «cache». Mais
l'évolution du matériel tend à rendre la barre «entier trop gros» de
plus en plus haute. Mais là, on est dans des considérations
d'implémentation. Je m'arrête.

Est-ce correct ?

Attends de voir un langage fonctionnel pur (comme Haskell par
exemple) : tu ne peux tout simplement pas modifier la valeur d'une
variable une fois qu'elle est crée !-)


On laissera ça pour ... une autre vie :-)


Pourquoi ? C'est intéressant aussi, comme approche, et il y a beaucoup à
en apprendre.


Tu as raisons. Je disais ça parce que j'étais un peu fatigué. :-)


Mais s'il y a *que* des références à des objets, cela veut dire que le
simple entier "1" en Python est un réalité un objet plus complexe
qu'un simple int ou char en C. Son codage binaire par exemple sera
plus complexe que le naturel "00000001" ?


C'est rien de le dire. A ton avis, pourquoi on les mets en cache ?

Tiens, si tu veux en savoir plus, essaie ça dans ton shell Python:

a = 1
for name in dir(a):
print name, " : ", getattr(a, name)


Je viens de le faire. Pfou !!! Effectivement, mon pauvre petit objet 1
est une usine à gaz à lui tout seul !!! Ça c'est l'impression générale.

En revanche, j'avoue ne pas comprendre exactement le résultat de ce
script. J'ai l'impression qu'en gros, on affiche tous les attributs de
l'objet référencé par a (d'après ce que j'ai cru comprendre en me
renseignant sur la fonction dir()). Pour moi, un attribut, c'est une
variable interne à l'objet. Donc, on peut afficher la valeur de
l'attribut en faisant a.attribut. Et bien, par exemple avec ceci :

a = 1 # avec dir(a) je vois par exemple qu'il y a l'attribut __add__
a.__add__
<method-wrapper '__add__' of int object at 0x816df38>




J'ai un résultat incompréhensible pour moi. C'est quoi ce résultat ? Ça
ne ressemble ni à un entier, ni une séquence etc.


Une autre chose me taraude. En C, une variable admet un contenu ou
valeur qui est codé(e) en binaire dans une zone contiguë de la mémoire.
En Python, cela a-t-il un sens de parler de contenu / valeur d'un objet
? J'en arrive à me dire que non.
- J'ai l'impression qu'on peut à la limite parler de contenu : un objet
dans la mémoire doit bien être stocké quelque part. En plus j'ai
l'impression que le stockage se fait dans des zones qui ne sont même pas
forcément contiguës.
- Mais je me demande si ça a toujours un sens de parler de valeur d'un
objet.

Par exemple, en C, je m'étais «amusé» à faire un tout petit programme
qui affiche le code binaire du contenu d'une variable. Est-ce possible
de faire la même chose en Python, c'est-à-dire afficher le code binaire
que contient un objet ? (si la question à un sens en Python)


--
François


PS : À un moment du parlais de "OP". Ça veut dire quoi ?



Avatar
Francois
C'est le problème de la différence entre l'égalité (de valeur) et
l'identité. Selon les types, les deux peuvent être équivalents ou non.

Dis-toi bien que la grande majorité des objets sont 1/ mutables, et 2/
avec un état beaucoup plus complexe qu'une simple valeur. La notion
d'égalité de valeur est arbitraire (en ce qu'elle est définie par le
type), celle d'identité ne l'est pas (deux objets sont identiques si
c'est le même objet, point, barre).

[couic]

a = [1, 2, 3]
b = [1, 2, 3]

A ce stade a et b sont égaux. Mais c'est accidentel. Doivent-ils être
identiques ? je peux vouloir modifier b sans modifier a

b[0] = 42

Si a et b avaient été identique, j'aurais, avec cette instruction,
modifié a également. Ainsi que toutes les autres listes de valeur
[1,2,3] existentes. Pas forcément ce que je voulais...


Ok. J'ai l'impression qu'en gros, il faut retenir ceci :

Avec

a = <truc>
b = <truc>

(où <truc> est un objet) dans la grande majorité des cas (pour la
plupart des objets <truc>), a et b se réfèrent à des objets distincts.
C'est par exemple toujours le cas quand <truc> est un objet mutable.
Quand <truc> est un objet entier pas trop gros, où finalement seul sa
valeur nous intéresse et qui n'est pas mutable, alors dans ce cas
particulier a et b se réfèrent exactement au même objet qui se trouvera
dans le «cache». Mais, finalement, ce cas particulier est un *détail*.
Les raisons de ce détail, tu les as dites : un entier est non mutable
donc on se moque éperdument de son identité, autant prendre la même
quand l'entier est déjà créé avant. Quand l'entier est trop «gros», on
retombe dans le cas général pour ne pas encombrer le «cache». Mais
l'évolution du matériel tend à rendre la barre «entier trop gros» de
plus en plus haute. Mais là, on est dans des considérations
d'implémentation. Je m'arrête.

Est-ce correct ?

Attends de voir un langage fonctionnel pur (comme Haskell par
exemple) : tu ne peux tout simplement pas modifier la valeur d'une
variable une fois qu'elle est crée !-)


On laissera ça pour ... une autre vie :-)


Pourquoi ? C'est intéressant aussi, comme approche, et il y a beaucoup à
en apprendre.


Tu as raisons. Je disais ça parce que j'étais un peu fatigué. :-)


Mais s'il y a *que* des références à des objets, cela veut dire que le
simple entier "1" en Python est un réalité un objet plus complexe
qu'un simple int ou char en C. Son codage binaire par exemple sera
plus complexe que le naturel "00000001" ?


C'est rien de le dire. A ton avis, pourquoi on les mets en cache ?

Tiens, si tu veux en savoir plus, essaie ça dans ton shell Python:

a = 1
for name in dir(a):
print name, " : ", getattr(a, name)


Je viens de le faire. Pfou !!! Effectivement, mon pauvre petit objet 1
est une usine à gaz à lui tout seul !!! Ça c'est l'impression générale.

En revanche, j'avoue ne pas comprendre exactement le résultat de ce
script. J'ai l'impression qu'en gros, on affiche tous les attributs de
l'objet référencé par a (d'après ce que j'ai cru comprendre en me
renseignant sur la fonction dir()). Pour moi, un attribut, c'est une
variable interne à l'objet. Donc, on peut afficher la valeur de
l'attribut en faisant a.attribut. Et bien, par exemple avec ceci :

a = 1 # avec dir(a) je vois par exemple qu'il y a l'attribut __add__
a.__add__
<method-wrapper '__add__' of int object at 0x816df38>




J'ai un résultat incompréhensible pour moi. C'est quoi ce résultat ? Ça
ne ressemble ni à un entier, ni une séquence etc.


Une autre chose me taraude. En C, une variable admet un contenu ou
valeur qui est codé(e) en binaire dans une zone contiguë de la mémoire.
En Python, cela a-t-il un sens de parler de contenu / valeur d'un objet
? J'en arrive à me dire que non.
- J'ai l'impression qu'on peut à la limite parler de contenu : un objet
dans la mémoire doit bien être stocké quelque part. En plus j'ai
l'impression que le stockage se fait dans des zones qui ne sont même pas
forcément contiguës.
- Mais je me demande si ça a toujours un sens de parler de valeur d'un
objet.

Par exemple, en C, je m'étais «amusé» à faire un tout petit programme
qui affiche le code binaire du contenu d'une variable. Est-ce possible
de faire la même chose en Python, c'est-à-dire afficher le code binaire
que contient un objet ? (si la question à un sens en Python)


--
François


PS : À un moment du parlais de "OP". Ça veut dire quoi ?



Avatar
Bruno Desthuilliers
C'est le problème de la différence entre l'égalité (de valeur) et
l'identité. Selon les types, les deux peuvent être équivalents ou non.

Dis-toi bien que la grande majorité des objets sont 1/ mutables, et 2/
avec un état beaucoup plus complexe qu'une simple valeur. La notion
d'égalité de valeur est arbitraire (en ce qu'elle est définie par le
type), celle d'identité ne l'est pas (deux objets sont identiques si
c'est le même objet, point, barre).

[couic]

a = [1, 2, 3]
b = [1, 2, 3]

A ce stade a et b sont égaux. Mais c'est accidentel. Doivent-ils être
identiques ? je peux vouloir modifier b sans modifier a

b[0] = 42

Si a et b avaient été identique, j'aurais, avec cette instruction,
modifié a également. Ainsi que toutes les autres listes de valeur
[1,2,3] existentes. Pas forcément ce que je voulais...


Ok. J'ai l'impression qu'en gros, il faut retenir ceci :

Avec

a = <truc>
b = <truc>

(où <truc> est un objet) dans la grande majorité des cas (pour la
plupart des objets <truc>), a et b se réfèrent à des objets distincts.


Attention: a = 1 est un sucre syntaxique pour a = int(1). Donc, c'est:

a = UneClasse()
b = UneClasse()

C'est par exemple toujours le cas quand <truc> est un objet mutable.
Quand <truc> est un objet entier pas trop gros, où finalement seul sa
valeur nous intéresse et qui n'est pas mutable, alors dans ce cas
particulier a et b se réfèrent exactement au même objet qui se trouvera
dans le «cache». Mais, finalement, ce cas particulier est un *détail*.
Les raisons de ce détail, tu les as dites : un entier est non mutable
donc on se moque éperdument de son identité, autant prendre la même
quand l'entier est déjà créé avant. Quand l'entier est trop «gros», on
retombe dans le cas général pour ne pas encombrer le «cache». Mais
l'évolution du matériel tend à rendre la barre «entier trop gros» de
plus en plus haute. Mais là, on est dans des considérations
d'implémentation. Je m'arrête.

Est-ce correct ?


Minus la correction ci-dessus, oui.

Mais s'il y a *que* des références à des objets, cela veut dire que
le simple entier "1" en Python est un réalité un objet plus complexe
qu'un simple int ou char en C. Son codage binaire par exemple sera
plus complexe que le naturel "00000001" ?


C'est rien de le dire. A ton avis, pourquoi on les mets en cache ?

Tiens, si tu veux en savoir plus, essaie ça dans ton shell Python:

a = 1
for name in dir(a):
print name, " : ", getattr(a, name)


Je viens de le faire. Pfou !!! Effectivement, mon pauvre petit objet 1
est une usine à gaz à lui tout seul !!!


Pour un entier, oui, ça relève de l'usine à gaz. Mais c'est le prix à
payer pour avoir une certaine uniformité (en l'occurrence, que tout ce
que tu peux manipuler en Python soit un objet).

Ça c'est l'impression générale.

En revanche, j'avoue ne pas comprendre exactement le résultat de ce
script. J'ai l'impression qu'en gros, on affiche tous les attributs de
l'objet référencé par a


A peu près tous (dir() ne retourne pas nécessairement l'intégralité des
attributs).

(d'après ce que j'ai cru comprendre en me
renseignant sur la fonction dir()). Pour moi, un attribut, c'est une
variable interne à l'objet.


Un attribut, c'est un objet que tu peux atteindre depuis un autre objet
en utilisant la syntaxe objet.nom_attribut (ou son équivalent
getattr(objet, 'nom_attribut')).

Donc, on peut afficher la valeur


la représentation, telle que définie par la méthode __repr__ de la
classe de l'attribut.

de
l'attribut en faisant a.attribut. Et bien, par exemple avec ceci :

a = 1 # avec dir(a) je vois par exemple qu'il y a l'attribut __add__
a.__add__
<method-wrapper '__add__' of int object at 0x816df38>




J'ai un résultat incompréhensible pour moi. C'est quoi ce résultat ? Ça
ne ressemble ni à un entier, ni une séquence etc.


C'est une méthode. Un objet méthode. Rappelle-toi qu'en Python, *tout*
est objet - fonctions, méthodes, classes etc compris. Essaie aussi ça,
tant que tu y es:

def func(): pass
dir(func)


Une autre chose me taraude. En C, une variable admet un contenu ou
valeur qui est codé(e) en binaire dans une zone contiguë de la mémoire.
En Python, cela a-t-il un sens de parler de contenu / valeur d'un objet
? J'en arrive à me dire que non.


Dans l'absolu, ça n'a effectivement aucun sens. Tout ce qu'un objet
'contien', ce sont des références sur d'autres objets.

- J'ai l'impression qu'on peut à la limite parler de contenu : un objet
dans la mémoire doit bien être stocké quelque part. En plus j'ai
l'impression que le stockage se fait dans des zones qui ne sont même pas
forcément contiguës.


Use the code, Luke.

- Mais je me demande si ça a toujours un sens de parler de valeur d'un
objet.


Tu peux considérer qu'en objet, globalement, valeur == état. Mais bon,
même en C, pour un char*, quelle est sa "valeur" ? l'adresse contenu
dans la variable, ou les données contenues à l'adresse contenue dans la
variable ?-) Et pour un pointeur sur un struct composé de pointeurs sur
des structs ? Quelle est sa "valeur" ?

Par exemple, en C, je m'étais «amusé» à faire un tout petit programme
qui affiche le code binaire du contenu d'une variable. Est-ce possible
de faire la même chose en Python, c'est-à-dire afficher le code binaire
que contient un objet ? (si la question à un sens en Python)


En Python, non, je ne vois pas comment tu pourrais faire une chose
pareille. En C, tu pourrais écrire une extension qui fasse une chose
similaire pour l'implémentation C des objets Python, mais ça n'aurait
aucun sens a priori.




Avatar
Bruno Desthuilliers
(snip)

Attention, si tu mets le PS après la signature, la plupart des lecteurs
vont le faire sauter !-)

PS : À un moment du parlais de "OP". Ça veut dire quoi ?


"Original Poster". Toi en l'occurrence.

Avatar
Francois
Attention: a = 1 est un sucre syntaxique pour a = int(1). Donc, c'est:

a = UneClasse()
b = UneClasse()


Arg ! Oui. Merci, de la rigueur, de la rigueur ! Il n'y a que comme ça
qu'on progresse. :-)


- J'ai l'impression qu'on peut à la limite parler de contenu : un
objet dans la mémoire doit bien être stocké quelque part. En plus j'ai
l'impression que le stockage se fait dans des zones qui ne sont même
pas forcément contiguës.


Use the code, Luke.


Je n'ai pas compris ? Peut-être une référence à Star Wars ? :-)


Tu peux considérer qu'en objet, globalement, valeur == état. Mais bon,
même en C, pour un char*, quelle est sa "valeur" ? l'adresse contenu
dans la variable, ou les données contenues à l'adresse contenue dans la
variable ?-) Et pour un pointeur sur un struct composé de pointeurs sur
des structs ? Quelle est sa "valeur" ?


Pour le char*, j'aurais dit que la valeur est l'adresse contenu dans la
variable. Mais pour les autres ?


Bon, je crois que j'ai déjà beaucoup abusé de ton temps. Je te remercie
mille fois pour ton aide précieuse Bruno. J'étais parti sur une mauvaise
pente avec mon approche à la C. Maintenant, grâce à toi, je pense être
dans la bonne direction. Mais il y a encore beaucoup de travail, ce qui
est normal.

Allez, juste une dernière chose. As-tu des bonnes références (livres,
docs en ligne etc. [je préfère les livres, mais bon...]) à me proposer
sur Python ? En fait, ce que j'aimerais bien, c'est une présentation
assez formelle, c'est-à-dire avec des définitions précises, qui permette
de faire apparaître la cohérence du langage. En fait, je suis un matheux
à la base (mais je vais bien quand même :-) ). J'ai donc l'habitude
d'avoir des définitions précises comme socle de départ. C'est ce genre
de présentation que j'aimerais bien trouver : un peu formelle et très
rigoureuse, un peu comme en maths. J'avoue que dans ma modeste
expérience en apprentissage de langage, j'ai toujours été gêné par le
manque de précision des définitions. Par exemple, avec le C, je sentais
parfois qu'il y avait une terminologie rigoureuse, mais je trouvais les
définitions souvent assez floues et contradictoires parfois d'une source
à l'autre et finalement j'étais un peu perdu.

begin{digression}


- "objet = zone de stockage dans la mémoire"
- "variable = objet portant un nom"

Donc, j'en déduis qu'un tableau est une variable. Je vais dans le fclc,
on me dit : "oui, oui un tableau est une variable". Très bien. Je
regarde le K&R et j'y vois écris noir sur blanc : "un tableau n'est pas
une variable". Je suis perdu. :-((

Une des rares doc que j'ai trouvée qui a une approche assez rigoureuse
comme j'aime (enfin qui s'en rapproche le plus), c'est sur le net que je
l'ai trouvée : "Introduction au langage C" de Bernard Cassagne. Il y
avait des trucs du genre :

« Règle :
Tout identicateur de type "tableau de x" apparaissant dans une
expression est converti en une valeur constante dont :
- le type est "pointeur vers x" ;
- la valeur est l'adresse du premier élément du tableau.»

Puis plein de petits exemples simples pour comprendre qu'effectivement
cette règle était vraiment bien appliquée à chaque fois, même dans t[2]
qui est converti en *(t+2) donc ça colle etc. Ça m'avait bien plus et
bien aidé a comprendre.

end{digression}

Bref, si tu as des références un peu dans le style que j'ai essayé de
décrire, ça m'intéresse. :-)

Pour l'instant, j'ai seulement : "Apprendre à programmer avec Python" de
Swinnen qui est *parfait pour commencer* je trouve. J'aimerais un truc
un peu plus formel maintenant.


Merci encore.

--
François


Avatar
Bruno Desthuilliers
Attention: a = 1 est un sucre syntaxique pour a = int(1). Donc, c'est:

a = UneClasse()
b = UneClasse()


Arg ! Oui. Merci, de la rigueur, de la rigueur ! Il n'y a que comme ça
qu'on progresse. :-)


- J'ai l'impression qu'on peut à la limite parler de contenu : un
objet dans la mémoire doit bien être stocké quelque part. En plus
j'ai l'impression que le stockage se fait dans des zones qui ne sont
même pas forcément contiguës.


Use the code, Luke.


Je n'ai pas compris ? Peut-être une référence à Star Wars ? :-)



CPython est un logiciel libre, tu peux télécharger le code source (s'il
n'était pas déjà inclus dans ton installation de Python - ça dépend de
comment tu l'a installé) et le consulter. Le code source d'un logiciel
est toujours la documentation la plus exacte que tu puisse trouver (à
défaut d'être la plus facilement compréhensible !-).

Accessoirement, la doc de Python comprend une partie qui aborde
l'implémentation C.

Tu peux considérer qu'en objet, globalement, valeur == état. Mais bon,
même en C, pour un char*, quelle est sa "valeur" ? l'adresse contenu
dans la variable, ou les données contenues à l'adresse contenue dans
la variable ?-) Et pour un pointeur sur un struct composé de pointeurs
sur des structs ? Quelle est sa "valeur" ?


Pour le char*, j'aurais dit que la valeur est l'adresse contenu dans la
variable.


Ce qui est techniquement la bonne réponse. D'un autre côté, quand tu
l'utilise pour une chaine (ce qui est l'usage le plus courant de ce type
en C), la valeur utile est la chaine, pas l'adresse de son premier
élément...

Mais pour les autres ?


Bin oui, pour les autres... Ce que je voulais dire, c'est que la notion
de "valeur" d'une variable n'est pas forcément si évidente à définir.


Bon, je crois que j'ai déjà beaucoup abusé de ton temps.


Dans la mesure où personne n'est en train de me mettre un flingue sur la
tempe pour m'obliger à répondre, y a pas d'abus...

Allez, juste une dernière chose. As-tu des bonnes références (livres,
docs en ligne etc. [je préfère les livres, mais bon...]) à me proposer
sur Python ? En fait, ce que j'aimerais bien, c'est une présentation
assez formelle, c'est-à-dire avec des définitions précises, qui permette
de faire apparaître la cohérence du langage.


Dans ce cas, le meilleur point de départ est la grammaire formelle du
langage.

En fait, je suis un matheux
à la base (mais je vais bien quand même :-) ). J'ai donc l'habitude
d'avoir des définitions précises comme socle de départ. C'est ce genre
de présentation que j'aimerais bien trouver : un peu formelle et très
rigoureuse, un peu comme en maths.


=> grammaire du langage.

J'avoue que dans ma modeste
expérience en apprentissage de langage, j'ai toujours été gêné par le
manque de précision des définitions.


Va falloir t'y faire. En général, il semble que les personnes avec une
forte culture mathématique soient plus attirées vers les langages
fonctionnels (Haskell, ML etc), ce "paradigme" étant un des seuls (avec
le modèle relationnel, utilisé dans les bases de données SQL) à découler
d'une théorie mathématique clairement définie. Les approches procédurale
et objet sont beaucoup plus informelle, et généralement plus prisées des
ingénieurs (obsevation sociologique qui ne s'appuie sur aucune étude
sérieuse, of course).

Par exemple, avec le C, je sentais
parfois qu'il y avait une terminologie rigoureuse, mais je trouvais les
définitions souvent assez floues et contradictoires parfois d'une source
à l'autre


Oui, c'est aussi un des problèmes : en l'absence d'une théorie claire et
bien établie, la terminologie est elle-même un peu floue, et la
polysémie est assez courante.

Bref, si tu as des références un peu dans le style que j'ai essayé de
décrire, ça m'intéresse. :-)

Pour l'instant, j'ai seulement : "Apprendre à programmer avec Python" de
Swinnen qui est *parfait pour commencer* je trouve. J'aimerais un truc
un peu plus formel maintenant.


On en revient toujours au même point: les deux seules définitions
"formelles" d'un langage sont sa grammaire (quand il en existe une -
c'est heureusement le cas pour Python) et son implémentation.

Pour le reste, j'ai lu fort peu de livres sur Python, et je ne sais pas
si celui que tu cherches existe - la plupart de ceux que j'ai vu sont
plus orienté vers l'utilisation pratique que sur l'étude du formalisme
et du fonctionnement interne du langage. Tu peux regarder dans les
bouquins mentionnés sur python.org, mais je ne te garanti rien.

De toutes façons, tu a déjà toute la partie "language reference" de la
doc en ligne, ainsi que toute la partie sur les "new-style class". Ca
devrait t'occuper un certain temps !-)



Avatar
Francois
Quand tu parlais de Luke Skywalker, ça m'a fait penser à ce message que
j'ai vu quelque part sur le net et qui parait-il est un peu connu dans
le monde du Python. Je le trouve marrant. :-)

---------------------------------------------------
Comparaison de Perl et Python par Yoda

Sur la planète Dagobah.

Avec Yoda accroché dans son dos, Luke grimpe sur une des vignes qui
poussent dans le marais pour atteindre le laboratoire de statistiques de
Dagobah. Il y continue ses exercices, greppe, installe des nouveaux
paquets, se connecte en root, écrit des nouvelles versions de scripts en
Python pour remplacer des scripts Perl vieux de deux ans.

Yoda : Écris du code ! Oui. La force d’un programmeur découle de la
maintenabilité de son code. Mais méfies-toi de Perl ! Syntaxe laconique,
plus d’une manière de faire quelque chose ! Le côté obscur de la
maintenabilité Perl est. Si une seule fois par le chemin obscur tu
t’engages, pour toujours ta destinée sera marquée.

Luke : est-ce que Perl est mieux que Python ?

Yoda : non... non... non. Plus rapide, plus facile, plus séduisant.

Luke : mais comment saurais-je pourquoi Python est mieux que Perl ?

Yoda : tu sauras. Quand le code écrit il y a 6 mois de relire tu tenteras.
-----------------------------------------------------



Sinon, je garde dans un coin de ma tête tes remarques sur les langages
fonctionnels.

Pour le reste, j'ai lu fort peu de livres sur Python, et je ne sais pas
si celui que tu cherches existe - la plupart de ceux que j'ai vu sont
plus orienté vers l'utilisation pratique que sur l'étude du formalisme
et du fonctionnement interne du langage. Tu peux regarder dans les
bouquins mentionnés sur python.org, mais je ne te garanti rien.

De toutes façons, tu a déjà toute la partie "language reference" de la
doc en ligne, ainsi que toute la partie sur les "new-style class". Ca
devrait t'occuper un certain temps !-)


Très bien, Ça me fera travailler mon anglais un peu rouillé. :-)
Merci infiniment Bruno pour toutes tes réponses.

À bientôt...


--
François

1 2 3