namespaces - Les espaces de nom en Perl
Au commencement, dans les années soixante, n'importe quelle partie de votre
programme avait accès à n'importe quelle autre partie de votre programme. Il
s'est avéré que c'était un problème. Alors les concepteurs de langages ont
inventé les variables locales, lesquelles étaient seulement visibles dans une
petite partie du programme. De cette façon, les programmeurs utilisant une
variable x
pouvaient être sûr que personne ne pouvait trifouiller son
contenu dans leur dos. Ils pouvaient également être sûrs qu'en utilisant x
ils ne trifouillaient pas la variable de quelqu'un d'autre par erreur.
Tous les langages de programmation ont une philosophie et, de nos jours, la plupart de ces philosophies ont à voir avec la façon dont sont manipulés les noms de variables. Quelle variable est visible depuis quelle partie du programme, que veulent dire les noms, et quand ? Voila des détails d'une importance cruciale. Ces détails vont du baroque, dans des langages comme Lisp, au vraiment très baroque dans des langages comme C++. Perl se classerait plutôt dans la catégorie rococo.
Le problème avec Perl, ce n'est pas qu'il n'a pas de système de gestion des variables clairement défini mais plutôt qu'il a deux systèmes, tous deux à l'oeuvre en même temps. Il s'agit d'un grand secret à propos des variables Perl que beaucoup de gens apprennent trop tard : Perl a deux jeux de variables complètement séparés et indépendants. L'un et hérité de Perl 4 et l'autre est nouveau. Les deux jeux de variables sont les « variables de package » et les « variables lexicales ». Ils n'ont rien à voir entre eux.
Les variables de package sont historiquement les premières alors nous commencerons par elles. Puis nous verrons quelques problèmes avec les variables de package, et comment les variables lexicales introduites avec Perl 5 viennent résoudre ces problèmes. Enfin nous verrons comment faire en sorte que Perl découvre automatiquement les endroit où il se pourrait que vous ne manipuliez pas la variable que vous croyez, ce qui permet de trouver les erreurs avant qu'elles ne deviennent des bugs.
$x = 1
Ici, x
est une variable de package. Il y a deux choses importantes à savoir
à propos des variables de package : Les variables de package, c'est ce que
l'on obtient si on ne précise pas autre chose. Les variables de package sont
toujours globales.
« Global » signifie que ces variables de package sont toujours
visibles partout, dans tous les programmes. Une fois que vous avez fait $x =
1
, n'importe quelle partie du programme, même un sous-programme défini dans
un autre fichier, peut inspecter et modifier la valeur de $x
. Il n'y a pas
d'exception à cela; les variables de package sont toujours globales.
Les variables de package sont divisées en familles appelées packages.
Toutes les variables de package ont un nom en deux parties. Ces deux parties
sont un peu comme le nom de famille et le prénom des variables. Vous pouvez
appeler le président de la république française « Jacques », si vous voulez,
mais c'est en réalité une abréviation de son nom complet qui est « Jacques
Chirac ». De même, $x
a un nom complet qui est quelque chose comme
$main::x
. La partie main est l'indicateur de package, tout comme la
partie « Chirac » de « Jacques Chirac ». Jacques Chirac et Jacques Lacan sont
des personnes différentes, bien qu'elles se prénomment toutes les deux
« Jacques ». De la même façon, $Chirac::Jacques
et
$Lacan::Jacques
sont des variables différentes, et $main::x
et
$DBI::x
sont des variables différentes.
Vous êtes toujours autorisé à inclure la partie du nom de la variable qui désigne le package, et si vous le faites, Perl saura exactement de quelle variable vous parlez. Mais par souci de concision, on aime généralement omettre l'indicateur de package. Que se passe-t-il dans ce cas là ?
Si vous dites juste $x
, Perl considère que vous voulez parler de la variable
$x
dans le package en cours. Qu'est-ce que le package en cours ?
C'est normalement main
, mais vous pouvez le changer en écrivant :
package Mypackage;
dans votre programme. A partir de là, le package en cours est Mypackage
. Le
seul rôle du package en cours est d'affecter l'interprétation des variables de
package que vous nommez sans préciser de nom de package. Si le package en
cours est Mypackage
, alors $x
signifie en réalité $Mypackage::x
. Si
le package en cours est main
, alors $x
signifie en réalité $main::x
.
Si vous écrivez un module, disons le module MonModule
, vous mettrez
sûrement une ligne comme ceci au début de votre fichier :
package MonModule;
A partir de là, toutes les variables que vous utilisez se trouvent dans le
package MonModule
et vous pouvez être pratiquement sûr que ces variables
n'entrent pas en conflit avec les variables du reste du programme. Cela n'a
pas d'importance si vous et l'auteur de DBI
utilisez une variable appelée
$x
, parce que l'une sera alors $MonModule::x
et l'autre sera $DBI::x
.
Rappelez vous que les variables de package sont toujours globales. Même si
vous n'êtes pas dans le package DBI
, même si vous n'en avez jamais
entendu parlé, rien ne peut vous empêcher de lire et écrire
$DBI::errstr
. Vous n'avez rien à faire de spécial. $DBI::errstr
, comme
toutes les variables de package, est une variable globale et elle est
disponible globalement. Tout ce que vous avez à faire c'est de l'appeler par
son nom complet pour l'atteindre. Vous pourriez même dire :
package DBI; $errstr = 'Ha ha Tim!';
et cela modifierait $DBI::errstr
.
Il y a seulement trois autres choses à savoir à propos des variables de package (vous pouvez les ignorer pour une première lecture) :
main
. Ainsi $::x
équivaut à $main::x
, quel que soit x
.
%ENV
, Perl considère que vous parlez de %main::ENV
, même si
le package en cours n'est pas main
. Si vous voulez parler de %Fred::ENV
,
vous devez l'appeler explicitement, même si le package en cours est
Fred
. Parmi les autre noms ayant cette spécificité on trouve INC
, tous
les noms constitué d'un caractère de ponctuation tels que $_
et $$
ainsi
que @ARGV
, STDIN
, STDOUT
et STDERR
.
::
mais pas les noms de variables. On
peut avoir une variable appelée $DBD::Oracle::x
. Cela désigne la variable
x
dans le package DBD::Oracle
; cela n'a rien à voir avec le package
DBD
. Isaac Newton n'a pas de rapport avec Olivia Newton-John, et
Newton::Isaac
n'a pas de rapport avec Newton::John::Olivia
. Même s'il
apparaît qu'ils commencent tous deux par Newton
, l'apparence est trompeuse.
Newton::John::Olivia
est dans le package Newton::John
, pas dans le
package Newton.
C'est tout ce qu'il y a à savoir à propos des variables de package.
Les variables de package sont globales, ce qui est dangereux parce que vous ne pouvez jamais être sûr que quelqu'un n'est pas en train de les trifouiller dans votre dos. Jusqu'à Perl 4, toutes les variables était des variables de package, ce qui était embêtant. Perl 5 à apporté de nouvelles variables qui ne sont pas globales.
L'autre jeu de variables de Perl s'appelle les variables lexicales (nous
verrons pourquoi plus tard) ou les variables privées parce qu'elles sont
privées. On peut aussi les appeler parfois variables my parce qu'elles sont
toujours déclarées avec my
. Il est tentant de les appelées
« variables locales » puisque leur effet est limité à une petite
partie du programme, mais ne le faites pas ! Les gens pourraient croire que
vous parlez du l'opérateur local
de Perl que nous verrons plus
tard. Lorsque vous voulez une « variable locale » pensez à my
,
pas à local
.
La déclaration
my $x;
crée une nouvelle variable appelée x
, qui est presque totalement
inaccessible à la plupart des parties du programme (Tous ce qui se trouve en
dehors du bloc où la variable a été déclarée. Ce bloc est appelée la portée
de la variable. Si la variable n'est pas déclarée dans un bloc, sa portée va
de l'endroit où elle a été déclarée à la fin du fichier).
On peut aussi déclarer et initialiser une variable my en écrivant :
my $x = 119;
On peut en déclarer et initialiser plusieurs à la fois :
my ($x, $y, $z, @args) = (5, 23, @_);
Voyons un exemple où les variables privées vont être utiles. Examinez ce sous-programme :
sub affiche_rapport { @liste_employes = @_; foreach $employe (@liste_employes) { $salaire = salaire($employee); affiche_rapport_partiel($employe, $salaire); } }
S'il se trouve que salaire
utilise aussi une variable appelée
$employe
, ce sera la même variable que celle utilisée dans
affiche_rapport
et le fonctionnement pourrait bien être perturbé.
Les deux programmeurs responsables de affiche_rapport
et
salaire
devront se consulter pour être sûr de ne pas utiliser les
mêmes variables. C'est un problème. En fait, même dans un projet de taille
moyenne, c'est un problème intolérable.
La solution consiste à utiliser les variables my
:
sub affiche_rapport { my @liste_employes = @_; foreach my $employe (@liste_employes) { my $salaire = salaire($employee); affiche_rapport_partiel($employe, $salaire); } }
my @liste_employes
crée une nouvelle variable de liste qui est
totalement inaccessible en dehors de la fonction affiche_rapport
.
for my $employes
crée une nouvelle variable scalaire qui est
totalement inaccessible en dehors de la boucle foreach
, tout comme
my $salaire
. Vous n'avez pas à vous préoccuper de savoir si les
autres fonctions du programme trifouillent ces variables, elles ne le
peuvent pas. Elles ne savent pas où les trouver car les noms ont une
signification différente en dehors de la portée des déclarations
my
. Ces « variables my » sont parfois appelées
« lexicales » car leur portée dépend uniquement du texte du
programme lui-même et pas des détails de son exécution comme par exemple
l'ordre dans lequel les choses sont effectuées. On peut déterminer cette
portée en observant le code source, sans même savoir ce qu'il fait. Lorsque
vous voyez une variable, chercher une déclaration my
plus haut dans
le même bloc. Si vous n'en voyez pas dans le plus petit bloc, regardez dans
le bloc plus large qui le contient et ainsi de suite jusqu'à ce que vous en
trouviez une. Si vous n'en trouvez pas, alors la variable est une variable
de package.
Les variables my
ne sont pas des variables de package. Elles ne
font pas partie d'un package et n'ont pas d'indicateur de package. Le
package en cours n'a aucun effet sur la façon dont elles sont interprétées.
Voici un exemple :
my $x = 17;
package A; $x = 12;
package B; $x = 20;
# $x vaut maintenant 20. # $A::x et $B::x sont toujours indéfinies
package A
change le package en cours, mais comme $x
fait
référence à une variable lexicale, et non à une variable de package,
$x=12
n'a aucun effet sur $A::x
. De même, après
package B
, $x=20
modifie la variable lexicale et non
l'une des variables de package.
À la fin du fichier, la variable lexicale $x
contient 20 et les
variables de package $main::x
, $A::x
, et
$B::x
sont toujours indéfinies. Si vous en aviez eu besoin, vous
auriez toujours pu y accéder en utilisant leurs noms complets.
La phrase à retenir est : « Les variables de package sont des
variables globales. » Pour les variables privées, il faut utiliser
my
.
Presque tout le monde sait qu'il y a une fonction local
en rapport
avec les variables locales. Qu'est-ce que c'est, et qu'est-ce que ça a à
voir avec my
? La réponse est simple, mais étrange :
my
crée une variable locale.
local
ne fait pas cela.
D'abord, voici ce que fait vraiment local $x
: ça sauve la
valeur actuelle de la variable de package $x
dans un endroit sûr
et ça la remplace par une nouvelle valeur ou par undef
si aucune
nouvelle valeur n'a été spécifiée. L'ancienne valeur est restaurée
lorsqu'on quitte le bloc en cours. Les variables affectées sont des
variables de package auxquelles on attribue une valeur local
e.
Mais les variables de package sont toujours globales, et une une variable
de package local
e ne fait pas exception. Pour voir la différence,
essayer ceci :
$lo = 'global'; $m = 'global'; A();
sub A { local $lo = 'AAA'; my $m = 'AAA'; B(); }
sub B { print "B ", ($lo eq 'AAA' ? 'peut' : 'ne peut pas') , " voir la valeur de lo assignée par A.\n";
print "B ", ($m eq 'AAA' ? 'peut' : 'ne peut pas') , " voir la valeur de m assignée par A.\n"; }
Cela affiche :
B peut voir la valeur de lo assignée par A. B ne peut pas voir la valeur de m assignée par A.
Que s'est-il passé ? La déclaration local
dans A
a sauvé une nouvelle
variable temporaire, AAA
, dans la variable de package $lo
. L'ancienne
valeur, global
, sera restaurée une fois que A
sera terminée, mais avant
que cela se produise, A
appelle B
. B
n'a pas de problème pour accéder
au contenu de $lo
puisque $lo
est une variable de package et les
variables de package sont toujours accessibles de partout. Ainsi elle voit la
valeur AAA
assignée par A
.
Au contraire, my
a créé une nouvelle variable, $m
, de portée
lexicalement limitée, qui est uniquement visible à l'intérieur de la fonction
A
. En dehors de A
, $m
garde son ancienne valeur : elle fait
référence à la variable de package $m
qui est toujours global
e. C'est la
variable que B
voit. Elle ne voit pas la valeur AAA
parce que la
variable qui a cette valeur est une variable lexicale et existe seulement à
l'intérieur de A
.
Comme local
ne crée en fait pas de variable locale, il n'est pas très
utile. Si, dans l'exemple ci-dessus, B
venait à modifier la valeur de
$lo
, alors la valeur assignée pas A
serait écrasée. C'est exactement ce
que nous ne voulons pas qu'il se produise. Nous voulons que chaque fonction
ait ses propres variables qui soient inaccessibles par les autres. C'est ce
que fait my
.
Pourquoi local
existe alors ? La réponse est à 90% historique. Les
premières version de Perl n'avait que des variables globales. local
était facile à implémenter et a été ajouté à Perl 4 comme une solution
partielle au problème des variables locales. Plus tard, dans Perl 5, plus de
travail a été accompli, et de vraies variables locales ont été ajoutées au
langage. Mais le nom local
était déjà pris, alors la nouvelle
fonctionnalité a été appelée my
. my
a été choisie parce
qu'elle suggère l'idée de privé et aussi parce que c'est très court. La
petitesse du mot est supposée vous encourager à l'utiliser à la place de
local
. my
est également plus rapide que local
.
Toujours utiliser my
; ne jamais utiliser local
.
Facile, non ?
À chaque déclaration my
, Perl crée une toute nouvelle variable. Ce
code, par exemple, affiche cinquante fois x=1
.
for (1 .. 50) { my $x; $x++; print "x=$x\n"; }
Vous obtenez un nouveau $x
initialisé à undef
à chaque tour
de boucle.
Si la déclaration était à l'extérieur de la boucle, elle ne serait effectuée qu'une seule fois, nous n'aurions donc qu'une seule variable.
{ my $x; for (1 .. 50) { $x++; print "x=$x\n"; } }
Cela affiche x=1
, x=2
, x=3
, ... x=50
.
Vous pouvez utiliser cela pour un truc utile : dans une fonction qui a
besoin de mémoriser une valeur d'un appel à l'autre. Par exemple considérons
un générateur de nombres aléatoires (comme la fonction rand
de Perl). Un
générateur de nombres aléatoires classique possède une graine (NDT: ``seed''
en anglais). Cette graine est simplement un nombre. Quand vous demandez un
nombre aléatoire, la fonction fait des opérations arithmétiques qui brouillent
la graine, et elle renvoie le résultat. Elle sauve également le résultat et
l'utilise comme graine lors de l'appel suivant.
Voici le code typique : (Je l'ai volé au C ANSI standard, mais il n'est pas terrible, alors ne l'utilisez pas pour quelque chose d'important).
$seed = 1; sub my_rand { $seed = int(($seed * 1103515245 + 12345) / 65536) % 32768; return $seed; }
Et sa sortie typique :
16838 14666 10953 11665 7451 26316 27974 27550
Mais il y a un problème, $seed
est une variable globale, ce qui signifie
que nous devons vérifier que personne ne la modifie par inadvertance. Ou bien
il pourrait y toucher en connaissance de cause, ce qui aurait des conséquences
sur le reste du programme. Qu'adviendrait-il, par exemple, s'il s'agissait
d'un programme de jeu et que quelqu'un vienne tripoter le générateur de
nombres aléatoires ?
Mais nous ne pouvons pas déclarer $seed
comme un variable my
dans la fonction :
sub my_rand { my $seed; $seed = int(($seed * 1103515245 + 12345) / 65536) % 32768; return $seed; }
En effet elle serait initialisée à undef
chaque fois que nous
appelons my_rand
. Nous avons besoin de retenir sa valeur entre
les appels de my_rand
.
Voici la solution :
{ my $seed = 1; sub my_rand { $seed = int(($seed * 1103515245 + 12345) / 65536) % 32768; return $seed; } }
La déclaration est en dehors de la fonction, ainsi elle ne se produit qu'une
fois, lorsque le programme est compilé, pas à chaque fois que la fonction est
appelée. Mais c'est une variable my
, et elle est dans un bloc, ce qui la
rend accessible uniquement au code situé à l'intérieur du bloc. my_rand
est la seule autre chose dans le bloc, donc la variable $seed
est seulement
accessible à la fonction my_rand
.
$seed
est parfois appelée une variable statique parce qu'elle reste la même
entre les appels de fonction (et aussi parce il y a une fonctionnalité
équivalente en C qui est spécifiée par le mot-clé static
).
(NDT: si vous savez traduire ce titre, faites moi signe :)
Vous ne pouvez pas déclarer une variable my
si son nom est un signe de
ponctuation, comme $_
, @_
ou $$
. Vous ne pouvez pas non plus déclarer
ainsi les variables de référence arrière $1
, $2
, etc. Les auteurs de
my
ont pensé que cela prêterait à confusion.
Bien sûr, vous ne pouvez pas dire my $DBI::errstr
, car se serait
contradictoire, cela reviendrait à dire que la variable de package
$DBI::errstr
est à présent une variable lexicale. Mais vous pouvez dire
local $DBI::errstr
, ce qui sauvegarde la valeur actuelle de $DBI::errstr
et fait en sorte qu'elle soit restituée à la fin du bloc.
Depuis Perl 5.004, on peut écrire :
foreach my $i (@list) {
Ce qui confine le $i
dans la portée de la boucle.
De même,
for (my $i=0; $i<100; $i++) {
limite la portée de $i
à la boucle for
.
Si vous écrivez une fonction et que vous voulez qu'elle ait des variables
privées, vous devez déclarer les variables avec my
. Que se passe-t-il si
vous oubliez ?
sub function { $x = 42; # Oops, on aurait du écrire my $x = 42. }
Dans ce cas, votre fonction modifie la variable de package globale $x
. Si
vous utilisez cette variable pour autre chose, ce sera un désastre pour votre
programme.
Les versions récentes de Perl on un protection optionnelle contre cela que vous pouvez activer si vous le souhaiter. Si vous mettez
use strict 'vars';
en haut de votre programme, Perl va exiger que ces variables de package
aient un indicateur de package explicite. Le $x
dans $x=42
n'a pas
d'indicateur, alors le programme ne va même pas se compiler. Le compilateur va
abandonner et donner le message d'erreur :
Global symbol "$x" requires explicit package name at ...
Si vous voulez que $x
soit une variable my
privée, vous pouvez revenir
en arrière et ajouter le my
. Si vous voulez vraiment utiliser la variable
de package globale, vous pouvez remplacer par
$main::x = 42;
ou un autre nom de package approprié.
Dire simplement use strict
active strict vars
, et plusieurs
autres vérifications. Voir perldoc strict
pour plus de détails.
À présent supposons que vous écrivez le module Algorithms::KnuthBendix
et
que vous voulez la protection de strict vars
mais que vous craignez de ne
pas pouvoir finir le module à cause de crampes aux doigts à force de taper
$Algorithms::KnuthBendix::Error
tout le temps.
Vous pouvez sauver vos doigts en disant à strict vars
de faire une
exception :
package Algorithms::KnuthBendix; use vars '$Error';
Cela évite que la variable $Algorithms::KnuthBendix::Error
cause une
erreur due à strict vars
si vous y faites référence par son nom court
$Error
.
Vous pouvez aussi désactiver strict vars
pour la portée du bloc en
écrivant :
{ no strict 'vars'; # strict vars est inactif jusqu'à la fin du bloc ... }
Les variables de package sont toujours globales. Elles ont un nom et un
indicateur de package. Vous pouvez omettre les indicateurs de package, auquel
cas Perl utilise le package par défaut, que vous pouvez définir par la
déclaration package
. Pour des variables privées, utilisez my
. N'utilisez
pas local
, c'est obsolète.
Vous devriez éviter les variables globales parce que ça peut être dur d'être sûr que deux parties du programme n'utilisent pas les variables des autres par erreur.
Pour éviter d'utiliser des variables globales par accident, ajoutez use
strict 'vars'
à votre programme. Cela vérifie que toutes vos variables sont
soit déclarées comme privées, soit qualifiées explicitement, soit
explicitement déclarées avec use vars
.
Certains se sont plaint de ma maxime « Ne jamais utiliser
local
. ». Mais dans 97% des cas, cette maxime est parfaitement
exacte. local
peut être utile, mais rarement, alors je les ai
laissées de côté car l'objectif d'un tutoriel est de présenter 97% d'utilité
dans 50% d'espace.
J'étais quand même embêté d'avoir beaucoup de mails rébarbatifs me disant « Tu
as oublié de mentionner que local
peut servir à ceci ou cela, tu sais ».
Alors j'ai écrit l'article Sept usages utiles de local pour me débarrasser
des gens qui me fatiguaient avec local
.
Voici un autre sujet intéressant que j'ai laissé de côté par souci de clarté et pour ne pas surcharger. J'ai reçu un mail de Robert Watkins à propos d'un programme qu'il avait écrit et qui ne fonctionnait pas. Voici à quoi ressemblait le bogue :
my $x;
for $x (1..5) { s(); }
sub s { print "$x, " }
Robert voulait afficher 1, 2, 3, 4, 5,
mais ça ne marchait pas. À
la place, cela affichait , , , , ,
. Où étaient donc passées les
valeurs de $x
?
Le truc ici c'est que normalement, quand vous écrivez quelque chose comme ça :
for $x (...) { }
Perl veut confiner la valeur de la variable d'index à l'intérieur de la
boucle. Si $x
est une variable de package, il fait comme si vous aviez
écrit :
{ local $x; for $x (...) { } }
En revanche, si $x
est une variable lexicale, il fait comme si vous
aviez écrit :
{ my $x; for $x (...) { } }
cela signifie que la variable d'index de boucle ne sera pas propagée aux sous-programmes, même si elle est dans la portée de la déclaration originale.
J'aurais probablement pu éviter de faire aussi long puisque la page de manuel perlsyn décrit cela très bien :
« ...la variable est implicitement locale à la boucle et reprend sa valeur précédente à la sortie de la boucle. Si la variable était précédemment déclaré par my, elle utilise cette variable au lieu de celle qui est globale, mais elle est toujours locale à la boucle. (A noter qu'une variable de portée lexicale peut poser des problèmes si vous avez un sous-programme ou une déclaration de format à l'intérieur de la boucle qui y fait référence.) »
À mon avis, donner une portée lexicale à la variable d'index était sûrement
une erreur. Si vouliez ça, vous auriez écrit for my $x ...
à la
place. J'aurais aimé que ça localise la variable lexicale : ça aurait sauvé
la valeur de la variable lexicale avant la boucle et ça l'aurait restauré
après la boucle. Mais pour des raisons techniques c'est impossible parce que
ça ne marche pas non plus :
my $m; { local $m = 12; ... }
Le local
échoue avec ce message d'erreur :
Can't localize lexical variable $m...
Il y a eu une discussion sur P5P à propos de l'éventualité de rendre cela possible, mais ça n'a pas l'air trivial.
Ajouté le 05/01/2001 : Il y a une petite saloperie qui prends les gens par surprise. Soit le programme suivant :
use strict 'vars'; my @lines = <>; my @sorted = sort backwards @lines; print @sorted;
sub backwards { $b cmp $a }
Ici nous n'avons déclaré ni $a
, ni $b
, ce sont donc des
variables globales. En fait, il est nécessaire qu'elles soient globales
parce que l'opérateur sort
doit être capable de les affecter pour
la fonction backwards
. Pourquoi strict
ne provoque-t-il
pas une erreur ?
Les variables $a
et $b
sont épargnées par la vérification de
strict vars
exactement pour cette raison.
Mark Jason Dominus
http://perl.plover.com/FAQs/Namespaces.html
(Attention : ce document est dynamique et choisit sa langue en fonction de celles demandées par votre navigateur.)
Alex Marandon <al@alpage.org>