Scoping dans les boucles Python 'for'


177

Je ne pose pas de questions sur les règles de portée de Python; Je comprends généralement comment la portée fonctionne en Python pour les boucles. Ma question est de savoir pourquoi les décisions de conception ont été prises de cette manière. Par exemple (sans jeu de mots):

for foo in xrange(10):
    bar = 2
print(foo, bar)

Ce qui précède imprimera (9,2).

Cela me semble étrange: «foo» ne contrôle que la boucle, et «bar» a été défini à l'intérieur de la boucle. Je peux comprendre pourquoi il pourrait être nécessaire que «bar» soit accessible en dehors de la boucle (sinon, les boucles for auraient des fonctionnalités très limitées). Ce que je ne comprends pas, c'est pourquoi il est nécessaire que la variable de contrôle reste dans la portée après la sortie de la boucle. D'après mon expérience, cela encombre simplement l'espace de noms global et rend plus difficile la recherche des erreurs qui seraient détectées par des interprètes dans d'autres langues.


6
Si vous ne voulez pas que la forboucle encombre votre espace de noms global, enveloppez-la dans une fonction. Des fermetures à gogo!
jathanism

24
Sauf si vous exécutez une boucle dans l'espace de noms global (rare), cela encombre un espace de noms local .
Glenn Maynard

3
Si cela n'existait pas, comment continueriez-vous le traitement plus tard au point que vous avez laissé dans la boucle? Définissez simplement la variable de contrôle avant la boucle?
endolith

9
@endolith Ouais ... Pourquoi ne pas l'exiger?
Steven Lu

3
Eh bien, les gens vont juste préférer ce qu'ils ont l'habitude de faire. Je dirais que ce genre de chose blesse le codeur python qui s'habitue à ce genre de chose et doit passer par un processus douloureux lors du passage à une autre langue. Pour le reste d'entre nous, c'est un petit raccourci sympa, je suppose.
Steven Lu

Réponses:


107

La réponse la plus probable est que cela garde simplement la grammaire simple, n'a pas été une pierre d'achoppement pour l'adoption, et beaucoup ont été satisfaits de ne pas avoir à lever l'ambiguïté à laquelle appartient un nom lors de son attribution dans une construction de boucle. Les variables ne sont pas déclarées dans une portée, elles sont impliquées par l'emplacement des instructions d'affectation. Le globalmot-clé existe uniquement pour cette raison (pour signifier que l'affectation est effectuée à une portée globale).

Mettre à jour

Voici une bonne discussion sur le sujet: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

Les propositions précédentes de rendre les variables de boucle for locales à la boucle ont trébuché sur le problème du code existant qui repose sur le maintien de la valeur de la variable de boucle après la sortie de la boucle, et il semble que cela soit considéré comme une fonctionnalité souhaitable.

En bref, vous pouvez probablement le blâmer sur la communauté Python: P


2
Comment la grammaire serait-elle plus compliquée si la portée de la variable d'induction était limitée au corps de la boucle? Un tel changement se limiterait à l'analyse sémantique en Python, pas à sa grammaire.
Charles

6
Les boucles ne sont pas des blocs en Python. Ce type de changement de comportement nécessiterait soit de changer fondamentalement la grammaire, soit de fournir un cas particulier. Le concept entier d'une variable d'induction n'est pas non plus exprimé dans la grammaire courante. La grammaire fournit le contrat pour la façon dont l'interprète interprétera. Mon point est que je ne peux pas prévoir comment un changement de ce comportement peut être effectué sans rendre la grammaire plus compliquée. Tout est sans objet puisque l'effet secondaire de la décision de conception est devenu une fonctionnalité.
Jeremy Brown

1
Ce message ici mail.python.org/pipermail/python-dev/2005-September/056677.html donne plus de détails concernant la vitesse et les complications auxquelles M. Brown fait allusion.
rajesh

62

Python n'a pas de blocs, comme le font certains autres langages (tels que C / C ++ ou Java). Par conséquent, l'unité de portée en Python est une fonction.


3
Je suis confus - qu'est-ce qui empêche Python de cibler les boucles de la même manière que les fonctions sont définies?
chimeracoder

36
Ce n'est pas vraiment vrai, c'est juste que la grammaire ne devient pas folle. ( docs.python.org/reference/… ) "Un bloc est un morceau de texte de programme Python qui est exécuté comme une unité. Ce qui suit sont des blocs: un module, un corps de fonction et une définition de classe ..."
Jeremy Brown

1
@le revers, rien. Cela a simplement été jugé inutile.
habnabit

@Jeremy Brown - en effet. Bonne note.
atzz

6
@thebackhand - dans les langages avec des blocs, les forboucles de portée sont une extension naturelle d'un principe général. En Python, cela devrait être un cas spécial, et les cas spéciaux doivent être évités à moins qu'ils ne présentent des avantages convaincants.
atzz

39

Un cas vraiment utile pour cela est lors de l'utilisation enumerateet vous voulez le nombre total à la fin:

for count, x in enumerate(someiterator, start=1):
    dosomething(count, x)
print "I did something {0} times".format(count)

Est-ce nécessaire? Non, mais c'est pratique.

Une autre chose à savoir: dans Python 2, les variables dans les compréhensions de liste sont également divulguées:

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
9

Mais, la même chose ne s'applique pas à Python 3.


4
Vous auriez pu le faire vraisemblablement dans l' elsearticle, c.-à-d. else: print "I did something {0} times".format(count)- avant la disparition de la portée locale (qui n'existe pas en Python)
Nas Banov

3
Seul le deuxième exemple ne fonctionne pas dans Python 3, non? Le premier fait toujours? Des notes expliquant pourquoi il a été supprimé de Python 3?
endolith

7
for count, item in enumerate (a, start = 1): # index par défaut est de zéro
Tao Zhang

3
Le premier exemple, plutôt que d'être un bon cas d'utilisation, ressemble davantage à la preuve que cette règle de cadrage est dangereuse et ne doit pas être invoquée. Et si someiteratorest vide?
max

1
@Nas Bien qu'une elseclause puisse être utilisée dans ce cas, elle ne fonctionnerait pas en général puisque le corps de la boucle pourrait breakprématurément.
jamesdlin

2

Si vous avez une instruction break dans la boucle (et que vous souhaitez utiliser la valeur d'itération plus tard, peut-être pour récupérer, indexer quelque chose ou donner un statut), cela vous permet d'économiser une ligne de code et une affectation, il y a donc une commodité.


1

L'une des principales influences de Python est ABC , un langage développé aux Pays-Bas pour enseigner les concepts de programmation aux débutants. Le créateur de Python, Guido van Rossum, a travaillé sur ABC pendant plusieurs années dans les années 1980. Je ne sais presque rien sur ABC, mais comme il est destiné aux débutants, je suppose qu'il doit avoir un nombre limité de portées, un peu comme les premiers BASIC.


-1

Pour commencer, si les variables étaient locales aux boucles, ces boucles seraient inutiles pour la plupart des programmes du monde réel.

Dans la situation actuelle:

# Sum the values 0..9
total = 0
for foo in xrange(10):
    total = total + foo
print total

rendements 45. Maintenant, considérez le fonctionnement de l'affectation en Python. Si les variables de boucle étaient strictement locales:

# Sum the values 0..9?
total = 0
for foo in xrange(10):
    # Create a new integer object with value "total + foo" and bind it to a new
    # loop-local variable named "total".
    total = total + foo
print total

cède 0, car totalà l'intérieur de la boucle après l'affectation n'est pas la même variable qu'à l' totalextérieur de la boucle. Ce ne serait pas un comportement optimal ou attendu.


5
Je ne réponds pas à la question. L'OP posait des questions sur foo, pas sur total (ou bar dans leur exemple).
James Bradbury

6
@JamesBradbury totalet fooaurait toujours des liaisons locales de boucle dans le scénario de l'OP et la logique est la même.
Kirk Strauser

2
OP: "Je peux comprendre pourquoi il pourrait être nécessaire que 'bar' soit accessible en dehors de la boucle (sinon, les boucles for auraient des fonctionnalités très limitées). Ce que je ne comprends pas, c'est pourquoi il est nécessaire que la variable de contrôle reste dans la portée après la sortie de la boucle. " (c'est moi qui souligne)
James Bradbury

2
@JamesBradbury Vous avez peut-être raison, mais j'ai répondu à cela il y a trois ans et cela ne vaut probablement pas la peine d'en débattre maintenant.
Kirk Strauser
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.