Escopo no Python 'para' loops

177

Não estou perguntando sobre as regras de escopo do Python; Geralmente eu entendo como o escopo funciona em Python para loops. Minha pergunta é por que as decisões de design foram tomadas dessa maneira. Por exemplo (sem trocadilhos):

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

O acima será impresso (9,2).

Isso me parece estranho: 'foo' está realmente apenas controlando o loop, e 'bar' foi definido dentro do loop. Entendo por que pode ser necessário que 'bar' seja acessível fora do loop (caso contrário, os loops teriam funcionalidade muito limitada). O que não entendo é por que é necessário que a variável de controle permaneça no escopo após a saída do loop. Na minha experiência, ele simplesmente atravessa o espaço para nome global e dificulta a detecção de erros que seriam detectados por intérpretes em outros idiomas.

chimeracoder
fonte
6
Se você não deseja que o forloop atravesse seu namespace global, envolva-o em uma função. Encerramentos em abundância!
jathanism
24
A menos que você esteja executando um loop no espaço para nome global (incomum), está atrapalhando um espaço para nome local .
Glenn Maynard
3
Se isso não existisse, como você continuaria o processamento posteriormente no ponto em que parou dentro do loop? Basta definir a variável de controle antes do loop?
Endolith 31/08/12
9
@ endolith Sim ... Por que não exigir isso?
Steven Lu
3
bem, as pessoas vão preferir o que estão acostumadas a fazer. Eu diria que esse tipo de coisa prejudica o codificador python que se acostuma com esse tipo de coisa e precisa passar por um processo doloroso ao mudar para um idioma diferente. Para o resto de nós, acho que é um pequeno atalho.
Steven Lu

Respostas:

107

A resposta mais provável é que ela mantém a gramática simples, não foi um obstáculo para a adoção e muitos ficaram felizes por não ter de desambiguar o escopo ao qual um nome pertence ao atribuí-lo em uma construção de loop. As variáveis ​​não são declaradas dentro de um escopo, elas estão implícitas na localização das instruções de atribuição. A globalpalavra-chave existe apenas por esse motivo (para indicar que a atribuição é feita em um escopo global).

Atualizar

Aqui está uma boa discussão sobre o tópico: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

As propostas anteriores para tornar as variáveis ​​do loop for localizadas no loop depararam com o problema do código existente que depende da variável do loop, mantendo seu valor após sair do loop, e parece que isso é considerado um recurso desejável.

Em suma, você provavelmente pode culpar a comunidade Python: P

Jeremy Brown
fonte
2
Como a gramática seria mais complicada se o escopo da variável de indução fosse limitado ao corpo do loop? Essa mudança estaria confinada à análise semântica em Python, não à sua gramática.
Charles
6
Loops não são blocos em Python. Esse tipo de mudança de comportamento exigiria uma mudança fundamental da gramática ou um caso especial. Todo o conceito de uma variável de indução também não é expresso na gramática atual. A gramática fornece o contrato para a interpretação do intérprete. Meu argumento é que não posso prever como uma mudança nesse comportamento pode ser feita sem tornar a gramática mais complicada. É tudo discutível desde que o efeito colateral da decisão de design se tornou um recurso.
Jeremy Brown
1
Esta publicação aqui mail.python.org/pipermail/python-dev/2005-Setembro/056677.html fornece mais detalhes sobre velocidade e complicações às quais o Sr. Brown faz alusão.
Rajesh
62

O Python não possui blocos, assim como algumas outras linguagens (como C / C ++ ou Java). Portanto, a unidade de escopo no Python é uma função.

atzz
fonte
3
Estou confuso - o que impede o Python de escopo de loops da mesma maneira que as funções têm escopo?
Chimeracoder
36
Não é verdade, mas a gramática não fica doida. ( docs.python.org/reference/… ) "Um bloco é uma parte do texto do programa Python que é executado como uma unidade. A seguir, são blocos: um módulo, um corpo de função e uma definição de classe ..."
Jeremy Brown
1
@ thebackhand, nada. Foi apenas considerado desnecessário.
habnabit 31/08/10
@ Jeremy Brown - de fato. Boa nota.
atzz
6
@ thebackhand - em idiomas com blocos, os forloops de escopo são uma extensão natural de um princípio geral. No Python, teria que ser um caso especial, e casos especiais devem ser evitados, a menos que tenham benefícios atraentes.
atzz 31/08/10
39

Um caso realmente útil para isso é quando enumeratevocê usa e deseja a contagem total no final:

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

Isso é necessário? Não. Mas, com certeza é conveniente.

Outra coisa a ter em atenção: no Python 2, as variáveis ​​nas compreensões de lista também são vazadas:

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

Mas, o mesmo não se aplica ao Python 3.

carl
fonte
4
Você poderia ter feito isso presumivelmente na elsecláusula, ie. else: print "I did something {0} times".format(count)- antes que o escopo local (que não existe no Python) desapareça
Nas Banov 01/09/10
3
Apenas o segundo exemplo não funciona no Python 3, certo? O primeiro ainda faz? Observa por que foi removido do Python 3?
Endolith 31/08/12
7
para count, item no enumerar (a, start = 1): # o índice padrão é zero
Tao Zhang
3
O primeiro exemplo, em vez de ser um bom caso de uso, parece mais a evidência de que essa regra de escopo é perigosa e não deve ser invocada. E se someiteratorestiver vazio?
max
1
@Nas Embora uma elsecláusula possa ser usada neste caso, ela não funcionaria em geral, já que o corpo do loop poderia breakprematuramente.
Jamesdlin
2

Se você tiver uma instrução break no loop (e quiser usar o valor da iteração posteriormente, talvez para recuperar, indexar algo ou dar status), ela economizará uma linha de código e uma atribuição, para que haja uma conveniência.

Mac
fonte
1

Uma das principais influências do Python é o ABC , uma linguagem desenvolvida na Holanda para ensinar conceitos de programação para iniciantes. O criador de Python, Guido van Rossum, trabalhou no ABC por vários anos nos anos 80. Não sei quase nada sobre o ABC, mas como ele é destinado a iniciantes, suponho que ele deva ter um número limitado de escopos, bem como os primeiros BASICs.

kindall
fonte
-1

Para iniciantes, se as variáveis ​​fossem locais para loops, esses loops seriam inúteis para a maioria das programações do mundo real.

Na situação atual:

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

rendimentos 45. Agora, considere como a atribuição funciona no Python. Se as variáveis ​​de loop fossem estritamente locais:

# 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

produz 0, porque totaldentro do loop após a atribuição não é a mesma variável que totalfora do loop. Esse não seria um comportamento ideal ou esperado.

Kirk Strauser
fonte
5
Não estou respondendo a pergunta. O OP estava perguntando sobre foo, não total (ou barra em seu exemplo).
precisa
6
@ JamesBradbury totale fooainda teria ligações locais de loop no cenário do OP e a lógica é a mesma.
precisa
2
OP: "Entendo por que pode ser necessário que 'bar' seja acessível fora do loop (caso contrário, os loops teriam funcionalidade muito limitada). O que não entendo é por que é necessário que a variável de controle permaneça no escopo após a saída do loop ". (ênfase meu)
James Bradbury
2
@ JamesBradbury Você pode estar certo, mas eu respondi isso há três anos e provavelmente não vale a pena debater agora.
precisa