Typage dynamique et affectation

Objectifs

  • Connaître la notion de typage dynamique ;

  • Connaître le comportement de l'assignation pour Python et JavaScript.

Mise en situation

On peut distinguer différentes manières de typer les variables selon les langages. Certains langages ont un typage statique, d'autre un typage dynamique. Cela signifie que les premiers ne permettent pas de changer le type d'une variable après sa déclaration. C'est souvent le cas des langages de bas niveau, qui sont proches de la machine et de sa gestion mémoire. Les langages à typage dynamique sont eux beaucoup plus flexibles, ils permettent de changer le type d'une variable tout au long de l'exécution.

RappelJavaScript et Python : des langages au typage dynamique

JavaScript et Python sont des langages au typage dynamique : les variables peuvent changer de valeurs même si celles-ci ont un type différent. Dans ce cadre, les types sont portés par les valeurs et non les variables.

ExempleTypage dynamique

Ici, a est une variable qui change de type pour passer d'un entier à une chaîne de caractères.

1
a = 42
2
a = 'fezfe'

RappelType primitif

Les types primitifs sont les types de valeurs les plus simples qui ne contiennent qu'une information atomique.

ExempleTypes primitifs en Python et JavaScript

Il y a quatre types primitifs communs à Python et JavaScript :

  • les entiers,

  • les flottants,

  • les chaînes de caractères,

  • les booléens.

Complément

En JavaScript il y a trois autres types primitifs :

  • les BigInt,

  • les symboles,

  • le type undefined.

RappelType composé

Les types composés sont des types construits sur un ensemble de types primitifs, comme les listes, les tableaux ou les objets.

ExempleTypes composés en JavaScript

En JavaScript par exemple, deux exemples de types composés sont :

  • les tableaux : []

  • les objets (enregistrements) : {}

FondamentalComportement de l'assignation pour le typage dynamique

Lorsque l'on assigne une variable en utilisant une autre dans des langages au typage dynamique comme (Python, ou JavaScript), on ne copie pas la valeur : on copie une référence vers cette valeur.

Cette subtilité a des implications très fortes lorsque l'on traite avec des types composés.

SyntaxeTester le référencement d'une valeur par deux variables en JavaScript

En JavaScript, l'opérateur d'égalité permet de vérifier que deux variables référencent la même valeur ou non.

Exemple

1
let quine = 10000
2
let bonus = quine
3
console.log('Equalité:', quine === bonus)
4
5
let loto = [100000]
6
let listeNumero = loto
7
console.log('Equalité:', loto === listeNumero)

On obtient :

1
Equalité: true
2
Equalité: true
Conservation de la référence lors de l'affectation d'une variable à une autreInformations[1]

SyntaxeTester le référencement d'une valeur par deux variables en Python

En Python la fonction id retourne l'identité de l'objet Python stockant la valeur sous-jacente.

Ainsi, avec l'opérateur d'égalité sur ces identités, on peut vérifier que deux variables référencent la même valeur ou non.

ExempleAffectation en Python

1
quine = 10000
2
bonus = quine
3
print('ID de quine:', id(quine))
4
print('ID de bonus:', id(bonus))
5
print('Equalité:', id(a) == id(b))
6
7
loto = [100000]
8
listeNumero = loto
9
print('ID de loto:', id(loto))
10
print('ID de listeNumero:', id(listeNumero))
11
print('Equalité:', id(loto) == id(listeNumero))

On obtient :

1
ID de quine: 140510876310096
2
ID de bonus: 140510876310096
3
Equalité: True
4
ID de loto: 140080656991808
5
ID de listeNumero: 140080656991808
6
Equalité: True

La subtilité est que quine et bonus ne contiennent pas tous les deux une copie de la valeur 10000, mais ils référencent la même case mémoire, qui contient la valeur 10000.

FondamentalComportement de la modification de valeur

Lorsque l'on modifie une valeur en JavaScript ou en Python à partir d'une variable :

  • S'il s'agit d'un type primitif, la valeur est copiée puis modifiée. En d'autres termes, la référence change.

  • S'il s'agit d'un type composé, on ne change pas de référence : seules les références de valeurs du type composés sont modifiées, s'il s'agit de types primitifs. Sinon, la même règle s'applique, et ainsi de suite.

ExempleModification d'un entier en Python

En exécutant cela :

1
quine = 10000
2
bonus = quine
3
print('ID de quine:', id(quine))
4
print('ID de bonus:', id(bonus))
5
print('Equalité:', id(quine) == id(bonus))
6
7
bonus = bonus + 1
8
print('ID de quine:', id(quine))
9
print('ID de bonus:', id(bonus))
10
print('Equalité:', id(quine) == id(bonus))

On obtient :

1
ID de quine: 140713190459984
2
ID de bonus: 140713190459984
3
Equalité: True
4
ID de quine: 140713190459984
5
ID de bonus: 140713190459888
6
Equalité: False

Ce qui montre que dans le cas de modification d'une valeur de type primitif, l'identité (la référence) change.

ExempleModification d'un entier en JavaScript

Le comportement est similaire en JavaScript :

1
let quine = 10000
2
let bonus = quine
3
console.log('Equalité:', quine === bonus)
4
5
bonus = bonus + 1
6
console.log('Equalité:', quine === bonus)

On obtient :

1
Equalité: true
2
Equalité: false
Copie de la valeur référencée lors de la modification d'un type primitifInformations[2]

ExempleModification d'un tableau en Python

En exécutant :

1
loto = [100000]
2
listeNumero = loto
3
print('ID de loto:', id(loto))
4
print('ID de listeNumero:', id(listeNumero))
5
print('Equalité:', id(loto) == id(listeNumero))
6
7
listeNumero.append(4240142)
8
print('ID de loto:', id(loto))
9
print('ID de listeNumero:', id(listeNumero))
10
print('Equalité:', id(loto) == id(listeNumero))

On obtient :

1
ID de loto: 140713190902976
2
ID de listeNumero: 140713190902976
3
Equalité: True
4
ID de loto: 140713190902976
5
ID de listeNumero: 140713190902976
6
Equalité: True

Ce qui montre que dans le cas de modification d'une valeur de type composé, la référence ne change pas.

Notez que le comportement peut surprendre, car le contenu loto est modifié au même titre que le contenu listeNumero, car les variables référencent le même contenu : aucune copie n'est effectuée.

ExempleModification d'un tableau en JavaScript

Le résultat est similaire en JavaScript :

1
let loto = [100000]
2
let listeNumero = loto
3
console.log('Equalité:', loto === listeNumero)
4
5
listeNumero.push(4240142)
6
console.log('Equalité:', loto === listeNumero)

On obtient :

1
Equalité: true
2
Equalité: true
Conservation de la référence lors de la modification d'une valeur de type composéInformations[3]

À retenir

Lors d'une affectation, on assigne des références et non des valeurs aux variables. Si on modifie une valeur à travers une variable, seules les références des variables de types primitifs sont modifiées.

Il ne suffit donc pas de faire une affectation pour copier un type composé : la variable originale et la nouvelle variable référenceront la même valeur (tableau, enregistrement, etc.), et les modifications seront reflétées pour les deux variables.