Accueil

Fonctions (et procédures): portée des variables et partage de références


COMPLEMENT DE COURS

Ce complément vous donne des précisions sur les fonctions. N'oubliez pas de garder les bonnes habitudes d'écriture du code, en continuant à bien choisir les paramètres de vos procédures et fonctions, et en gardant la structure habituelle (import, def, ligne de séparation,programme avec les input, appels de fonctions et print).

1. Portée des variables

Les paramètres des fonctions ainsi que les variables utilisées dans les fonctions sont des variables locales c'est à dire qu'elles ne sont connues qu'à l'intérieur de la définition de la fonction. On appelle portée d'une variable le bloc de code dans laquelle sa définition (ou valeur) est connue (on dit aussi visible). La portée d'une variable locale à une fonction est la fonction elle-même.

Soit la fonction reverse et son utilisation dans un programme de test:

Exemple


def reverse(l):
  res = ""
  for c in l:
    res = c+res
  return res

###############################
# programme de test
m = input("un mot")
print(reverse(m))

Que se-passe-t-il si vous ajoutez print(res) à la fin du programme ? et si vous ajoutez print(m) à la fin de la fonction reverse?

Ci-dessus, la variable m est une variable globale c'est à dire qu'elle est visible partout. On peut donc l'utiliser dans la fonction reverse, même si cela n'est pas recommandé. En effet, la fonction reverse doit permettre de renverser n'importe quelle chaîne de caractères, et c'est donc normal d'utiliser un paramètre pour désigner la chaîne sur laquelle elle s'applique.

Dans votre projet, vous utiliserez des variables globales pour simplifier les écritures et définir les caractéristiques de votre fenêtre turtle. Vous apprendrez petit à petit à bien choisir les paramètres de vos fonctions et quelles variables peuvent être globales.

2. Partage de références

Nous avons vu que le type liste est modifiable alors que le type str ne l'est pas. Essayez de taper les lignes suivantes dans votre fenêtre d'exécution python :

c ="coucou"
print(c[3])
c[3]="a"
print(c)

puis les lignes suivantes:

l=["c","o","u","c","o","u"]
print(l[3])
l[3]="a"
print(l)

On peut modifier le contenu d'une liste alors qu'on ne peut pas modifier un caractère dans une chaîne de caractères.
Une variable qui contient une liste contient en fait une référence vers cette liste, qui désigne la suite de cases mémoires contenant les valeurs de la liste, qui sont accéssibles à partir de cette référence. Quand on écrit l1=l2 où l2 est une liste, y a partage des références c'est à dire que l1 et l2 désignent la même liste et seront donc modifiées de la même façon. Tester les lignes suivantes dans la fenêtre d'exécution :

l=["c","o","u","c","o","u"]
l2=l
l[3]="a"
print(l)
print(l2)

3. Passage de paramètres par valeur

En Python, le passage de paramètres se fait par valeur, c'est à dire que la valeur du paramétre effectif est recopiée dans le paramètre formel de la fonction. Par conséquent, une fonction ne peut pas changer la valeur du paramètre effectif.

Cas des types non modifiables

def bidon1(x,y):
    x=x+y

def bidon2(m):
    m=m+"bidon"    
######
x=8
y=6
bidon1(x,y)
print("après bidon1",x)
m="exemple"
bidon2(m)
print("après bidon2",m)

Dans les print ci-dessus, les valeurs de x et m n'ont pas changé, puisque l'on a passé les valeurs d'un entier et d'une chaîne de caractères et que les types str et int ne sont pas modifiables.

Cas des types modifiables

Pour une liste, la valeur qui est recopiée est sa référence. Du coup, quand on passe un paramètre de type modifiable on peut modifier la valeur du paramètre effectif.

# insere x à sa place dans une liste triée l
def insere(x,li):
    i=0
    while i < len(li) and x>li[i]:
        i=i+1
    li.insert(i,x)
#   li=[8]
#   li[0]="c'est compliqué"

######
n = int(input("nb d'éléments"))
i=0
l=[]
while i < n:
    x=int(input("valeur ? "))
    insere(x,l)
    i=i+1
print(l)

Dans cet exemple, la variable locale li de la procédure insere partage la même référence que la variable globale l. Donc à la sortie de insere, la liste globale l a été modifiée et le print final affiche tous les éléments triés par ordre croissant.


Si l'on décommente les deux dernières lignes de la fonction insere cela ne change rien. En effet, l'instruction li=[8] alloue dans la mémoire une nouvelle liste qui vaut [8]. La variable locale li fait alors référence à cette nouvelle liste. A partir de ce moment, la variable locale li et la variable globale l ne partagent plus la même référence et donc une modification de la variable locale li ne change rien à la variable globale l.
Pour ceux qui connaissent C ou C++, il existe un passage de paramètre par adresse qui permet de modifier non seulement le contenu des cases mémoire mais aussi l'adresse elle-même. Avec ce passage par adresse, la liste globale l vaudrait ["c'est compliqué"] après exécution de la procédure insere.