Demat' iNal
Regarde bien dans les yeux le bout de code suivant
def foo():
x = 1
class bar:
x = x + 1
return bar
foo()
exécute le mentalement. Rien d'exceptionnel ?
Et pourtant si,
NameError: name 'x' is not defined
Maintenant essayons avec :
def foo():
x = 1
class bar:
y = x + 1
return bar
foo()
Et là… non, rien.
Intuitivement, j'imagine que le x = x + 1
rend Python tout confus, mais j'aimerai mettre le doigt sur une source officielle et je n'la trouve point. Sauras-tu faire mieux que moi ?
# Le moi d'après répond au moi d'avant
Posté par serge_sans_paille (site web personnel) . Évalué à 10.
D'après https://docs.python.org/3/reference/executionmodel.html#resolution-of-names :
en gros on rentre dans le scope de la classe. Ce scope introduit une variable locale
x
et donc toute référence àx
dans ce scope se fera en référançant la variable locale. Or lors de l'évaluation de la partie droite de l'assignation,x
n'est pas encore assigné, bamNameError
.[^] # Re: Le moi d'après répond au moi d'avant
Posté par flan (site web personnel) . Évalué à 2. Dernière modification le 08 mars 2023 à 21:41.
Tout à fait. Et un IDE qui ne soulignerait pas en rouge le x de x+1 serait à mon avis un très mauvais IDE pour Python.
[^] # Re: Le moi d'après répond au moi d'avant
Posté par gUI (Mastodon) . Évalué à 9.
Je pardonne plus facilement l'IDE que le codeur. Non mais sérieusement, c'est quoi cette horreur ?
En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.
[^] # Re: Le moi d'après répond au moi d'avant
Posté par Jean Gabes (site web personnel) . Évalué à 4.
J’avoue qu'en ait la question ne devrait même pas se poser. Peu importe la réponse, c'est non maintenable et ce genre de chose c'est direct rejeté en peer review ^
Python fait des choses bizarre parfois avec la portées des variables, faut rester simples, sinon on se prends les pieds dans le tapis très, très rapidement.
# Et pourtant c'est simple
Posté par Christophe B. (site web personnel) . Évalué à 4.
Comme toujours en python, enfin la plupart du temps, c'est simple
Imagine que Python utilise un dictionnaire ou une table correspondance pour gérer ses variables
Chaque fonction a droit a ses variables et donc sa table de correspondance.
Si on reprend ton exemple :
Création de la fonction foo
=> création et affectation de x a partir d'un entier
=> Création de la class bar
==> affectation de x à partir de x (qui n'existe pas dans la class bar …) => erreur
Je devrais abuser de ce forum et te conseiller un excellent livre sur Python :)
mais comme la 2eme édition ne va pas tarder à sortir … attends un peu ;)
[^] # Re: Et pourtant c'est simple
Posté par barmic 🦦 . Évalué à 5.
Pas tout à fait sinon la seconde méthode marcherait pas non plus.
Création de la fonction foo
=> création et affectation de x a partir d'un entier
=> Création de la class bar
==> le bloc crée une variable x donc on retire x de la table
==> affectation de x à partir de x => erreur
Du coup ça fonctionne :
Du coup ça plante :
Toutes les variables crées dans un bloque cachent les variables disponibles dès le début du bloque. Ce n'est pas la déclaration d'une variable à partir d'elle même qui pose problème, mais le fait d'utiliser une variable qui sera surchargée dans ce même bloque.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Et pourtant c'est simple
Posté par BAud (site web personnel) . Évalué à 5. Dernière modification le 09 mars 2023 à 12:01.
forcément, ça (dé-)bloque dans tout le bloc… tout comme la brique braque dans le bric à brac ;-)
[^] # Re: Et pourtant c'est simple
Posté par Christophe B. (site web personnel) . Évalué à 3. Dernière modification le 09 mars 2023 à 19:12.
J'ai répondu un peu trop rapidement …
Tu as raison d'ailleurs les dernières versions de python sont plus précises
Remarque : les underscores sont la pour respecter la cadrage
Donc python crée une variable locale et ne va pas rechercher dans les variable "globales" la valeur de x donc la recherche ne se fait que dans la portée "locale"
Alors que dans y = x + 1
x fait partie des variables "globales" de la classe bar car cette class a été déclarée à l'intérieur de la fonction et donc cela ne déclenche par d'erreur
Si en plus on demande au langage python de retourner une classe et pas une instance de classe
par curiosité :
cela retourne :
<class 'main.foo.<locals>.bar'>
Python version 3.11
PS: désolé pour la présentation, je comprends pas pourquoi les backquotes ne me font pas du beau code en python …
[^] # Re: Et pourtant c'est simple
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
PS : C'est le Markdown de LinuxFr qui casse parfois les pieds ; t'as bien pensé à sauter une ligne avant et après ?
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Et pourtant c'est simple
Posté par Benoît Sibaud (site web personnel) . Évalué à 3.
Corrigé, merci.
[^] # Re: Et pourtant c'est simple
Posté par Christophe B. (site web personnel) . Évalué à 2.
Aaaah merci beaucoup …
[^] # Re: Et pourtant c'est simple
Posté par BAud (site web personnel) . Évalué à 2. Dernière modification le 10 mars 2023 à 02:44.
l'explication est sur le wiki LinuxFr.org où il est indiqué :
pensez à ajouter une ligne blanche avant la balise ``` ;
merci Gil< de l'explication :-) (Gil et Oumph sont de bonne facture :p)
et puis bon, hein, si c'est pas assez clair, plaignez-vous ! (bah, je mettrai à jour la page avec vos remarques :p)
[^] # Re: Et pourtant c'est simple
Posté par warwick . Évalué à 1.
Et d’ailleurs on peut accéder dans le deuxième cas a y, qui a pour valeur 2
[^] # Re: Et pourtant c'est simple
Posté par GaMa (site web personnel) . Évalué à 7. Dernière modification le 09 mars 2023 à 15:03.
Ha mais non, on supprime rien.
C'est plutôt que la variable
x
dans le namespace de la classbar
"masque" la variablex
dans le namespace defoo
.Le fonctionnement global est celui ci:
Lors de la définition d'une fonction (ou d'une class ici), un "scope" (local) est créé.
Lors de la résolution de nom (la recherche de l’objet qui correspond à
x
), on cherche d'abord dans ce scope local. Si on le trouve pas et si la définition de la fonction à lieu dans un scope (par exemple à l'intérieur de l’exécution defoo()
), on cherche dans ce scope "parent". Si on le trouve toujours pas, on remonte à nouveau (si on peut). Enfin, on arrive au scope global (celui du module) et si on trouve toujours pas, on arrête avec une exceptionNameError
.La subtilité, c'est qu'on attend pas de faire un
x = 1
pour créer la variable x dans son scope. À la définition de la fonction, python "sait" qu'on va avoir besoin d'une variable x et donc il crée le slot (vide) dans le scope dès le début de l’exécution.Donc lors de la résolution de nom, il trouve
x
dans le scope local et donc arrête de chercher dans le scopes parents. Mais comme x n'est pas encore initialisé, bam !Donc c'est bien l'existence de la variable
x
dans le scope local qui masquex
dans les scopes parent même si l'initialisation dex
a lieu après la ligne qui plante.Ça donne des trucs rigolo :
nonlocal
permet de dire à python de ne pas créer de slot dans le scope local mais d'utiliser celui du scope parent (et seulement celui là).global
fait la même chose mais dit d'utiliser le scope global :Matthieu Gautier|irc:starmad
[^] # Re: Et pourtant c'est simple
Posté par fearan . Évalué à 7.
Et après y'en a encore pour dire que c'est plus lisible que perl…
perl a le
qui manque cruellement…
Il ne faut pas décorner les boeufs avant d'avoir semé le vent
[^] # Re: Et pourtant c'est simple
Posté par Christophe B. (site web personnel) . Évalué à 2.
Bien vu :)
de la pure mauvaise foi à l'état brut
mais bonne remarque très bien placée, il faut le reconnaître
respect
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.