Scoping nei loop 'for' di Python


177

Non sto chiedendo delle regole di scoping di Python; Comprendo in generale come funziona l'ambito in Python per i loop. La mia domanda è: perché le decisioni di progettazione sono state prese in questo modo. Ad esempio (nessun gioco di parole previsto):

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

Quanto sopra verrà stampato (9,2).

Questo mi sembra strano: 'foo' sta davvero controllando il loop e 'bar' è stato definito all'interno del loop. Posso capire perché potrebbe essere necessario che "bar" sia accessibile al di fuori del loop (altrimenti, i loop avrebbero funzionalità molto limitate). Quello che non capisco è perché è necessario che la variabile di controllo rimanga nell'ambito dopo la chiusura del ciclo. Nella mia esperienza, ingombra semplicemente lo spazio dei nomi globale e rende più difficile rintracciare gli errori che verrebbero rilevati dagli interpreti in altre lingue.


6
Se non vuoi che il forloop ingombra il tuo spazio dei nomi globale, avvolgilo in una funzione. Chiusure a bizzeffe!
jatanismo

24
A meno che tu non stia eseguendo un ciclo nello spazio dei nomi globale (non comune), sta ingombrando uno spazio dei nomi locale .
Glenn Maynard,

3
Se ciò non esistesse, come continueresti a elaborare successivamente nel punto in cui hai interrotto il ciclo? Basta definire la variabile di controllo prima del ciclo?
endolith

9
@endolith Sì ... Perché non richiederlo?
Steven Lu,

3
beh, la gente preferirà semplicemente ciò a cui è abituata. Direi che questo genere di cose fa male al programmatore di pitone che si abitua a questo genere di cose e deve passare attraverso un processo doloroso quando passa a una lingua diversa. Per il resto di noi, suppongo sia una piccola scorciatoia.
Steven Lu,

Risposte:


107

La risposta più probabile è che mantiene semplice la grammatica, non è stato un ostacolo per l'adozione e molti sono stati contenti di non dover chiarire l'ambito a cui appartiene un nome quando lo si assegna all'interno di un costrutto loop. Le variabili non sono dichiarate in un ambito, è implicito dalla posizione delle istruzioni di assegnazione. La globalparola chiave esiste proprio per questo motivo (per indicare che l'assegnazione viene eseguita in un ambito globale).

Aggiornare

Ecco una buona discussione sull'argomento: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

Le precedenti proposte per rendere le variabili for-loop locali al loop si sono imbattute nel problema del codice esistente che si basa sulla variabile loop che mantiene il suo valore dopo essere usciti dal loop e sembra che questa sia considerata una caratteristica desiderabile.

In breve, probabilmente puoi dare la colpa alla comunità Python: P


2
In che modo la grammatica sarebbe più complicata se l'ambito della variabile di induzione fosse limitato al corpo del loop? Un simile cambiamento si limiterebbe all'analisi semantica in Python, non alla sua grammatica.
Charles,

6
I loop non sono blocchi in Python. Questo tipo di cambiamento comportamentale richiederebbe o cambiare fondamentalmente la grammatica o fornire un caso speciale. Anche l'intero concetto di una variabile di induzione non è espresso nella grammatica attuale. La grammatica prevede il contratto per l'interpretazione dell'interprete. Il mio punto è che non posso prevedere come un cambiamento in questo comportamento possa essere fatto senza complicare la grammatica. È tutto controverso poiché l'effetto collaterale della decisione progettuale è diventato una caratteristica.
Jeremy Brown,

1
Questo post qui mail.python.org/pipermail/python-dev/2005-September/056677.html fornisce ulteriori dettagli sulla velocità e le complicazioni a cui allude il signor Brown.
rajesh,

62

Python non ha blocchi, così come altri linguaggi (come C / C ++ o Java). Pertanto, l'unità di scoping in Python è una funzione.


3
Sono confuso: cosa impedisce a Python di eseguire la scoping per i loop nello stesso modo in cui le funzioni sono mirate?
chimeracoder

36
Non proprio vero, è solo che la grammatica non impazzisce. ( docs.python.org/reference/… ) "Un blocco è un pezzo di testo del programma Python che viene eseguito come unità. I ​​seguenti sono blocchi: un modulo, un corpo di funzione e una definizione di classe ..."
Jeremy Brown

1
@ il rovescio, niente. È stato appena ritenuto non necessario.
habnabit

@Jeremy Brown - davvero. Buona nota
Atzz,

6
@thebackhand - nelle lingue con blocchi, i forloop di scoping sono una naturale estensione di un principio generale. In Python dovrebbe essere un caso speciale e casi speciali devono essere evitati a meno che non abbiano vantaggi convincenti.
Atzz,

39

Un caso davvero utile per questo è quando si utilizza enumeratee si desidera il conteggio totale alla fine:

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

È necessario? No. Ma è sicuramente conveniente.

Un'altra cosa da tenere presente: in Python 2, trapelano anche le variabili nella comprensione dell'elenco:

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

Ma lo stesso non si applica a Python 3.


4
Avresti potuto farlo presumibilmente nella elseclausola, vale a dire. else: print "I did something {0} times".format(count)- prima che scompaia l'ambito locale (che non esiste in Python)
Nas Banov

3
Solo il secondo esempio non funziona in Python 3, giusto? Il primo lo fa ancora? Note sul perché è stato rimosso da Python 3?
endolith

7
per conteggio, elemento in elenco (a, inizio = 1): # l'indice predefinito è da zero
Tao Zhang

3
Il primo esempio, piuttosto che essere un buon caso d'uso, sembra più l'evidenza che questa regola di scoping sia pericolosa e su cui non si debba fare affidamento. E se someiteratorfosse vuoto?
max

1
@Nas Anche elsese in questo caso è possibile utilizzare una clausola, non funzionerebbe in generale poiché il corpo del loop potrebbe breakprematuramente.
jamesdlin,

2

Se hai un'istruzione break nel ciclo (e vuoi usare il valore di iterazione in un secondo momento, forse per recuperare, indicizzare qualcosa o dare uno stato), ti salva una riga di codice e un'assegnazione, quindi c'è una comodità.


1

Una delle principali influenze di Python è ABC , un linguaggio sviluppato nei Paesi Bassi per insegnare concetti di programmazione ai principianti. Il creatore di Python, Guido van Rossum, ha lavorato alla ABC per diversi anni negli anni '80. Non so quasi nulla dell'ABC, ma poiché è destinato ai principianti, suppongo che debba avere un numero limitato di ambiti, proprio come i primi BASIC.


-1

Per cominciare, se le variabili fossero locali per i loop, quei loop sarebbero inutili per la maggior parte della programmazione del mondo reale.

Nella situazione attuale:

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

rese 45. Ora, considera come funziona l'assegnazione in Python. Se le variabili del ciclo fossero strettamente locali:

# 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

produce 0, perché totalall'interno del ciclo dopo l'assegnazione non è la stessa variabile totalesterna al ciclo. Questo non sarebbe un comportamento ottimale o previsto.


5
Non rispondere alla domanda. L'OP stava chiedendo informazioni sul foo, non sul totale (o sulla barra nel loro esempio).
James Bradbury,

6
@JamesBradbury totale fooavrebbe ancora collegamenti loop-local nello scenario dell'OP e la logica è la stessa.
Kirk Strauser,

2
OP: "Posso capire perché potrebbe essere necessario che" bar "sia accessibile al di fuori del loop (altrimenti, per i loop avrebbero una funzionalità molto limitata). Quello che non capisco è perché è necessario che la variabile di controllo rimanga nell'ambito dopo la chiusura del ciclo ". (enfasi mia)
James Bradbury,

2
@JamesBradbury Forse hai ragione, ma ho risposto tre anni fa e probabilmente non vale la pena di discutere ora.
Kirk Strauser,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.