3 votes

Deux occurrences d'une variable déclarée avec 'my' mais dans deux packages différents dans le même fichier

Je n'ai pas codé en Perl depuis longtemps. Maintenant, j'ai recommencé et je suis tombé sur une situation intéressante lors de la programmation de deux packages dans un seul fichier '.pm'. Jusqu'à présent, je pensais que si je définissais deux packages dans un fichier (par exemple ConfigData et RuntimeData), je pouvais définir une variable (déclarée avec 'my') avec le même nom (par exemple my $instance) pour chaque package sans conflit. Voici un code minimal pour illustrer ceci :

package Jabs::ConfigData;
use strict;
use warnings;

my $instance;
sub new {
        my ($class) = @_;
        unless (defined $instance) {
                $instance = bless {}, $class;
        }
        return $instance;
}
1;  # Fin de 'ConfigData'

package Jabs::RuntimeData;
use strict;
use warnings;

my $instance;
sub new {
        my ($class) = @_;
        unless (defined $instance) {
                $instance = bless {}, $class;
        }
        return $instance;
}
1;  # Fin de 'RuntimeData'

J'ai appelé perl -c test2.pm et j'ai reçu le message suivant en retour :

"my" variable $instance masque la déclaration antérieure dans le même scope à la ligne 27 de test2.pm.
test2.pm syntax OK

Maintenant, il n'est pas clair pour moi pourquoi le scope devrait être le même. Parce que 'my $instance' est défini dans deux packages différents. Je suis sur Ubuntu 24.04 LTS et Perl5 (révision 5 version 38 sous-version 2)

Est-ce que quelqu'un pourrait peut-être m'expliquer mon problème de compréhension ?

5voto

TLP Points 48922

Le perldoc -f my correspondant indique :

Un my déclare les variables répertoriées comme locales (lexicalement) dans le bloc, le fichier ou l'evalution

Le mot-clé package sous forme de package NOM n'inclut pas une telle portée. Bien que la documentation pour le package mentionne que vous pouvez déclarer un package avec un bloc, par exemple package NOM BLOC. Au bas se trouve ma démonstration de ces blocs.

perldoc perlmod élucide :

Une déclaration de package n'affecte que les symboles globaux dynamiques, y compris les noms de sous-routine et les variables sur lesquelles vous avez utilisé local(), mais pas les variables lexicales créées avec my(), our() ou state().


use strict;
use warnings;
use feature 'say';

package Conf {  # <-- début du bloc
    use strict;
    use warnings;

    my $instance;
    sub new {
            my ($class) = @_;
            unless (defined $instance) {
                    $instance = bless {}, $class;
            }
            $instance->{foo} = "Confs";
            return $instance;
    }
    1;
} # <-- fin du bloc FIN DE CONF

package eData { # <-- début du bloc
    use strict;
    use warnings;

    my $instance;
    sub new {
        my ($class) = @_;
        unless (defined $instance) {
                $instance = bless {}, $class;
        }
        $instance->{foo} = "eDatas";
        return $instance;
    }
    1;
} # <-- fin du bloc FIN DE EDATA

my $c1 = Conf->new;    # charge le premier $instance 
my $c2 = eData->new;   # charge l'autre $instance, variable et portée différentes

say $c1->{foo};
say $c2->{foo};

Sorties :

Confs
eDatas

2voto

brian d foy Points 71781

La réponse courte est que les variables de package et les variables lexicales sont deux systèmes de suivi de variables distincts qui n'ont rien à voir l'un avec l'autre.

Une variable de package est effectivement globale. Elle existe en tout temps et en tous lieux dans le programme tant que vous connaissez le nom de package entièrement qualifié (tel que main::foo). L'instruction package change simplement le package par défaut à utiliser. La fonction local remplace temporairement la valeur de la variable de package dans une portée (bloc contenant ou fichier).

Les variables lexicales sont entièrement contrôlées par la portée (bloc contenant ou fichier) qui contient la déclaration (my).

Dans votre cas, les deux instances de my $instance apparaissent dans la même portée (le fichier).

Mais, il semble que vous essayez de créer deux classes singleton où vous retournez soit la chose que vous avez déjà créée, soit vous la créez lors de la première utilisation.

La fonction state de Perl v5.10 vous permet de déplacer cette variable dans la subroutine. C'est une variable lexicale persistante. Et, l'opérateur // (défini-ou) vous permet d'utiliser une affectation binaire pour simplifier les choses :

use v5.12;

package Foo { # le block package nécessite v5.12, pas nécessaire pour le problème
    sub new {
            state $instance; # nécessite v5.10;
            return $instance //= bless {}, $_[0];
    }
}

Vous pouvez faire la même chose avant v5.10 en définissant votre new à l'intérieur d'une portée qui contient le $instance. Maintenant, $instance n'existe que dans cette portée :

package Foo;
    {
    my $instance;
    sub new {
            return $instance if defined $instance;
            $instance = bless {}, $_[0];
        }
    }

package main;

...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X