Pour être honnête, je comprends parfaitement les fermetures sauf que je n'ai jamais été clair sur ce qu'est exactement la chose qui est la "fermeture" et ce qui est si "fermeture" à son sujet. Je vous recommande d'abandonner la recherche d'une quelconque logique derrière le choix du terme.
Bref, voici mon explication :
def foo():
x = 3
def bar():
print x
x = 5
return bar
bar = foo()
bar() # print 5
L'idée clé ici est que l'objet fonction retourné par foo conserve un lien avec la var locale 'x' même si 'x' est sorti de la portée et devrait être défectueux. Ce lien concerne la variable elle-même, et pas seulement la valeur qu'elle avait à ce moment-là. Ainsi, lorsque bar est appelé, il imprime 5 et non 3.
Il faut également savoir que Python 2.x a une fermeture limitée : il n'y a aucun moyen de modifier 'x' à l'intérieur de 'bar' parce qu'écrire 'x = bla' déclarerait un 'x' local dans bar, et non une affectation à 'x' de foo. Il s'agit d'un effet secondaire de la déclaration assignment=de Python. Pour contourner ce problème, Python 3.0 introduit le mot-clé nonlocal :
def foo():
x = 3
def bar():
print x
def ack():
nonlocal x
x = 7
x = 5
return (bar, ack)
bar, ack = foo()
ack() # modify x of the call to foo
bar() # print 7