IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

exec et le namespace local en Python 3 ?


Sujet :

Python

  1. #1
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut exec et le namespace local en Python 3 ?
    Bonjour,

    Quelqu'un pourrait m'expliquer ce comportement en Python 3 ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    >>> def testexec(value):
    ...     exec('newvalue = ' + str(value) + '+ 10')
    ...     print(locals()['newvalue'])
    ...     return locals()['newvalue']
    ... 
    >>> print(testexec(5))
    15
    15
    >>> def testexec(value):
    ...     exec('newvalue = ' + str(value) + '+ 10')
    ...     print(locals()['newvalue'])
    ...     return newvalue
    ... 
    >>> print(testexec(5))
    15
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in testexec
    NameError: global name 'newvalue' is not defined
    Merci d'avance

  2. #2
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Je ne peux pas vraiment répondre à ta question, mais dans la doc de exec():
    The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.
    Et dans celle de locals():
    The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
    You've been warned

  3. #3
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour dividee,

    Désolé, je ne comprend pas...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    >>> def testexec(value):
    ...     exec('newvalue = ' + str(value) + '+ 10', globals(), locals())
    ...     print(locals()['newvalue'])
    ...     return newvalue
    ...
    >>> print(testexec(5))
    15
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in testexec
    NameError: global name 'newvalue' is not defined
    >>>
    Il semble que return ne regarde pas dans le namespace local...
    Le print(locals()['newvalue']) montre bien que c'est dans le dico.

    @+

  4. #4
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Bonjour PauseKawa,

    Citation Envoyé par PauseKawa Voir le message
    Il semble que return ne regarde pas dans le namespace local...
    Le print(locals()['newvalue']) montre bien que c'est dans le dico.
    Et bien oui, c'est bien de cela qu'ils parlent dans la doc. Le code exécuté par exec modifie le dictionnaire locals() ce que la doc dit de ne pas faire; les changements peuvent ne pas être visible de l'interpréteur.

    Ce test est éclairant, c'est la même chose qui se produit avec ton code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    >>> def f():
    ... 	locals()["a"]=42
    ... 	return a
    ... 
    >>> f()
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
      File "<interactive input>", line 3, in f
    NameError: global name 'a' is not defined
    >>> import dis
    >>> dis.dis(f)
      2           0 LOAD_CONST               1 (42) 
                  3 LOAD_GLOBAL              0 (locals) 
                  6 CALL_FUNCTION            0 
                  9 LOAD_CONST               2 ('a') 
                 12 STORE_SUBSCR         
     
      3          13 LOAD_GLOBAL              1 (a) 
                 16 RETURN_VALUE
    On voit qu'à l'adresse 13, l'opcode est LOAD_GLOBAL (au lieu de LOAD_FAST pour une variable locale). C'est parce que la compilation en bytecode se fait avant l'exécution. Le bytecode compiler n'a pas trouvé de définition locale de [g]a[/g], vu qu'elle est cachée dans la modification de locals(), et suppose donc que c'est une variable globale.

    Maintenant ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    >>> def f():
    ... 	a = 1
    ... 	locals()["a"]=42
    ... 	return a
    ... 
    >>> f()
    1
    >>> dis.dis(f)
      2           0 LOAD_CONST               1 (1) 
                  3 STORE_FAST               0 (a) 
     
      3           6 LOAD_CONST               2 (42) 
                  9 LOAD_GLOBAL              0 (locals) 
                 12 CALL_FUNCTION            0 
                 15 LOAD_CONST               3 ('a') 
                 18 STORE_SUBSCR         
     
      4          19 LOAD_FAST                0 (a) 
                 22 RETURN_VALUE
    Pourquoi cela ne fonctionne-t-il pas ici ? L'argument de LOAD_FAST/STORE_FAST est un entier, pas le nom de la variable (le nom est affiché entre parenthèses mais ça c'est juste dis qui est gentil). Cela suggère que les variables locales ne sont pas stockées par l'interpréteur sous forme d'une dictionnaire, mais plutôt une liste (ou une structure similaire). C'est assez logique quand on y pense, c'est plus rapide qu'un lookup dans un dictionnaire... L'affectation des indices aux variables se fait au moment de la compilation en bytecode. Le dictionnaire renvoyé par locals() est reconstruit à partir des cette liste et du tuple contenant les noms des variables locales (f.__code__.co_varnames), mais ce n'est pas la structure de données qui est utilisée par l'interpréteur.

  5. #5
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonsoir,

    Grand Merci dividee.
    J'avais bien compris que c'était un souci de block/namespace mais je n'avais pas les connaissances pour me sortir d'un return locals()['newvalue'].
    Ceci dit le passage en fonction d'exec en Python 3 me semble assez problématique.
    Je vais tenter d'ingurgiter (tester et retester) tout cela et essayer de faire le point sur les différences 2.x/3.x a ce niveau.

    @+

    Edit: Dans tout cela, et vu la 'toute puissance' d'exec, je me demande si cela ne vas pas finir déprécated au profit d'eval. Avis perso.

  6. #6
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour dividee,

    Je me permets de revenir sur le sujet pour quelques suppositions gratuites.

    Citation Envoyé par dividee Voir le message
    L'affectation des indices aux variables se fait au moment de la compilation en bytecode. Le dictionnaire renvoyé par locals() est reconstruit à partir des cette liste et du tuple contenant les noms des variables locales (f.__code__.co_varnames), mais ce n'est pas la structure de données qui est utilisée par l'interpréteur.
    Pour moi, jusqu’à maintenant, nous avions une recherche ascendante dans les namespaces locale, puis le globales etc...
    Pourrez ton supposer ici que c'est le bytecode qui dit ou rechercher ?
    Qu'il n'y a pas de 'vrais' recherche si ce n'est que le compilateur a regarder à la construction du bytecode si la variable est présente dans le block pour dire si c'est un LOAD_FAST ou un LOAD_GLOBAL, stocker la liste des locales ?
    Cela expliquerais pas mal de choses (read only, avertissements, absence d'erreur car la variable est considérée comme globale donc sa présence est possible lors de l’exécution, etc...) y compris le cas présent. Si la 'liste des locales' est dans le bytecode 'newvalue' n'y est effectivement pas. Mais rien n’empêche de le rajouter dans le dico locals(). Cela ne le rajouteras pas dans la liste de toute façon.
    On pourrais aller plus loin est penser que cela explique ce genre de comportement.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    >>> def foo(v, l=[]):
    ...     l.append(v)
    ...     print(l)
    ... 
    >>> foo(1)
    [1]
    >>> foo(2)
    [1, 2]
    Divagations ?

  7. #7
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725

  8. #8
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Oui c'est bien cela dont je parlais. La recherche ascendante dans les namespaces a bien lieu, mais elle a lieu lors de la compilation. Apparemment, il y a trois types d'accès aux variables au niveau du bytecode:
    • variables locales (LOAD_FAST): appartenant au namespace local
    • variables libres (LOAD_DEREF): appartenant à un namespace englobant mais pas global
    • variables globales (LOAD_GLOBAL): non trouvée lors de la compilation (sauf éventuellement dans le namespace global).


    A remarquer que la compilation peut avoir lieu a tout moment, suite à un import/eval/exec/compile, pas seulement au lancement du script.

    Au sujet du fait que les valeurs par défaut ne sont évaluées qu'une fois, je ne crois pas qu'il s'agit du même mécanisme. Elles sont évaluées lors de l'exécution, pas pendant la compilation. Un argument (l dans ton exemple) est une variable locale.

  9. #9
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour,

    C'est bien le fait qu'un argument soit une variable locale (Je parle bien des variables locales, pas du namespace*) qui me fais penser à cela pour les valeurs par défaut. Pourquoi seraient elles traitées autrement ?
    Comment est stocké un mutable dans la liste des variables locales ?
    Mais bon, pas trop de doc sur le sujet si ce n'est
    The default values are evaluated at the point of function definition in the defining scope
    (4. More Control Flow Tools)

    Et le célèbre Important warning
    The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes.
    (Même source)

    Je range donc l'utilisation (écriture) du dico locals() au sein d'une fonction dans mon dossier 'This function should be used for internal and specialized purposes only'

    Merci dividee.

    *
    The local namespace for a function is created when the function is called, and deleted when the function returns or raises an exception that is not handled within the function. (Actually, forgetting would be a better way to describe what actually happens.) Of course, recursive invocations each have their own local namespace.
    on est bien d'accord.

Discussions similaires

  1. exec Vs. exec in locals()
    Par shaiHulud dans le forum Général Python
    Réponses: 4
    Dernier message: 29/03/2015, 20h57
  2. modifier les namespace et les prefixes d'une enveloppe soap en python
    Par roadbecri dans le forum XML/XSL et SOAP
    Réponses: 0
    Dernier message: 12/01/2015, 11h07
  3. Utilisation de exec*() en python
    Par chedu06 dans le forum Général Python
    Réponses: 13
    Dernier message: 26/04/2010, 17h28
  4. "Bdd" locale en python...
    Par Mr Hyde dans le forum Général Python
    Réponses: 4
    Dernier message: 17/08/2005, 10h44
  5. Peux t'on créer une copie locale de l'objet partagé?
    Par Anonymous dans le forum CORBA
    Réponses: 8
    Dernier message: 16/04/2002, 16h20

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo