J'essaie de comprendre l'approche de Python à la portée variable. Dans cet exemple, pourquoi est-il f()
capable de modifier la valeur de x
, telle qu'elle est perçue à l'intérieur main()
, mais pas la valeur de n
?
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
Production:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
Réponses:
Certaines réponses contiennent le mot «copie» dans le contexte d'un appel de fonction. Je trouve cela déroutant.
Python ne copie pas les objets que vous passez au cours d' un appel de fonction jamais .
Les paramètres de fonction sont des noms . Lorsque vous appelez une fonction, Python lie ces paramètres à tous les objets que vous passez (via des noms dans la portée d'un appelant).
Les objets peuvent être mutables (comme les listes) ou immuables (comme les entiers, les chaînes en Python). Objet mutable que vous pouvez modifier. Vous ne pouvez pas changer un nom, vous pouvez simplement le lier à un autre objet.
Votre exemple ne concerne pas les étendues ou les espaces de noms , il concerne la dénomination, la liaison et la mutabilité d'un objet en Python.
Voici de belles images sur la différence entre les variables dans d'autres langages et les noms en Python .
la source
def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
.x = []
inf()
n'a aucun effet sur la listex
dans la fonction principale. J'ai mis à jour le commentaire pour le rendre plus précis.Vous avez déjà un certain nombre de réponses, et je suis globalement d'accord avec JF Sebastian, mais vous pourriez trouver cela utile comme raccourci:
Chaque fois que vous voyez
varname =
, vous créez une nouvelle liaison de nom dans la portée de la fonction. Quelle que soit la valeur à laquelle onvarname
était lié auparavant, elle est perdue dans cette portée .Chaque fois que vous voyez
varname.foo()
que vous appelez une méthodevarname
. La méthode peut modifier varname (par exemplelist.append
).varname
(ou, plutôt, l'objet quivarname
nomme) peut exister dans plusieurs portées, et comme il s'agit du même objet, toutes les modifications seront visibles dans toutes les portées.[notez que le
global
mot - clé crée une exception au premier cas]la source
f
ne modifie pas réellement la valeur dex
(qui est toujours la même référence à une instance d'une liste). Au contraire, cela modifie le contenu de cette liste.Dans les deux cas, une copie d'une référence est transmise à la fonction. À l'intérieur de la fonction,
n
se voit attribuer une nouvelle valeur. Seule la référence à l'intérieur de la fonction est modifiée, pas celle à l'extérieur.x
ne reçoit pas de nouvelle valeur: ni la référence à l'intérieur ni à l'extérieur de la fonction n'est modifiée. Au lieu de cela,x
la valeur de est modifiée.Étant donné que l'
x
intérieur et l'extérieur de la fonction font référence à la même valeur, les deux voient la modification. En revanche, l'n
intérieur et l'extérieur de la fonction font référence à des valeurs différentes après avoirn
été réaffecté à l'intérieur de la fonction.la source
Je renommerai les variables pour réduire la confusion. n -> nf ou nmain . x -> xf ou xmain :
Lorsque vous appelez la fonction f , le runtime Python fait une copie de xmain et l'affecte à xf , et affecte de la même manière une copie de nmain à nf .
Dans le cas de n , la valeur copiée est 1.
Dans le cas de x, la valeur copiée n'est pas la liste littérale [0, 1, 2, 3] . C'est une référence à cette liste. xf et xmain pointent sur la même liste, donc lorsque vous modifiez xf, vous modifiez également xmain .
Si, cependant, vous deviez écrire quelque chose comme:
vous constaterez que xmain n'a pas changé. C'est parce que, dans la ligne xf = ["foo", "bar"], vous avez changé xf pour pointer vers une nouvelle liste. Toutes les modifications que vous apportez à cette nouvelle liste n'auront aucun effet sur la liste vers laquelle pointe toujours xmain .
J'espère que cela pourra aider. :-)
la source
nf = 2
, où le nomnf
est modifié pour pointer vers2
. Les nombres sont immuables, les listes sont modifiables.C'est parce qu'une liste est un objet mutable. Vous ne définissez pas x sur la valeur de [0,1,2,3], vous définissez une étiquette pour l'objet [0,1,2,3].
Vous devez déclarer votre fonction f () comme ceci:
la source
x = x + [4]
place dex.append(4)
, vous ne verrez pas non plus de changement dans l'appelant, bien qu'une liste soit modifiable. Il s'agit de savoir s'il est effectivement muté.x += [4]
alorsx
est muté, tout comme ce qui se passe avecx.append(4)
, donc l'appelant verra le changement.n est un int (immuable), et une copie est passée à la fonction, donc dans la fonction vous modifiez la copie.
X est une liste (mutable), et une copie du pointeur est passée à la fonction donc x.append (4) change le contenu de la liste. Cependant, vous avez dit x = [0,1,2,3,4] dans votre fonction, vous ne changeriez pas le contenu de x dans main ().
la source
Si les fonctions sont réécrites avec des variables complètement différentes et que nous appelons id sur elles, cela illustre bien le point. Je ne l'ai pas compris au début et j'ai lu le message de jfs avec la bonne explication , alors j'ai essayé de comprendre / me convaincre:
z et x ont le même identifiant. Juste des balises différentes pour la même structure sous-jacente que l'article dit.
la source
Python est un pur langage pass-par-valeur si vous y pensez correctement. Une variable python stocke l'emplacement d'un objet en mémoire. La variable Python ne stocke pas l'objet lui-même. Lorsque vous passez une variable à une fonction, vous transmettez une copie de l'adresse de l'objet pointé par la variable.
Contrastez ces deux fonctions
Maintenant, quand vous tapez dans le shell
Comparez cela à goo.
Dans le premier cas, on passe une copie de l'adresse de cow à foo et foo a modifié l'état de l'objet qui y réside. L'objet est modifié.
Dans le second cas, vous passez une copie de l'adresse de la vache à goo. Ensuite, goo procède à la modification de cette copie. Effet: aucun.
J'appelle cela le principe de la maison rose . Si vous faites une copie de votre adresse et dites à un peintre de peindre la maison à cette adresse en rose, vous vous retrouverez avec une maison rose. Si vous donnez au peintre une copie de votre adresse et lui dites de la changer pour une nouvelle adresse, l'adresse de votre maison ne change pas.
L'explication élimine beaucoup de confusion. Python transmet les variables d'adresses stockées par valeur.
la source
Python est une copie par valeur de référence. Un objet occupe un champ en mémoire, et une référence est associée à cet objet, mais elle-même occupe un champ en mémoire. Et le nom / valeur est associé à une référence. Dans la fonction python, il copie toujours la valeur de la référence, donc dans votre code, n est copié pour être un nouveau nom, lorsque vous l'affectez, il a un nouvel espace dans la pile des appelants. Mais pour la liste, le nom a également été copié, mais il fait référence à la même mémoire (puisque vous n'attribuez jamais de nouvelle valeur à la liste). C'est une magie en python!
la source
Ma compréhension générale est que toute variable d'objet (telle qu'une liste ou un dict, entre autres) peut être modifiée via ses fonctions. Ce que je pense que vous ne pouvez pas faire est de réaffecter le paramètre - c'est-à-dire de l'affecter par référence dans une fonction appelable.
Cela est cohérent avec de nombreuses autres langues.
Exécutez le court script suivant pour voir comment cela fonctionne:
la source
J'avais modifié ma réponse des tonnes de fois et réalisé que je n'avais rien à dire, python s'était déjà expliqué.
Ce diable n'est pas la référence / valeur / mutable ou non / instance, espace de nom ou variable / liste ou str, C'EST LA SYNTAXE, LE SIGNE ÉGAL.
la source