inside - python closure example
¿Qué es exactamente lo que está contenido dentro de un cierre obj.__? (4)
Es el nuevo nombre de Python 3 para el antiguo func_closure
.
http://docs.python.org/3.0/whatsnew/3.0.html
Los atributos de función llamados
func_X
han sido renombrados para usar el formulario__X__
, liberando estos nombres en el espacio de nombres de atributo de función para los atributos definidos por el usuario. Parafunc_closure
,func_code
func_closure
,func_code
,func_defaults
,func_dict
,func_doc
,func_globals
,func_name
a__closure__
,__code__
,__defaults__
,__dict__
,__doc__
,__globals__
,__name__
, respectivamente.
En una palabra:
__closure__
es None
o una tuple
de celdas que contienen enlaces para las variables libres de la función.
Además, NO es escribible.
Referencia: http://docs.python.org/ref/types.html
Ejemplo Python <3 (entonces estoy usando func_closure
)
def foo():
x = "I am used"
y = "I am free"
z = "I am free too"
def bar(x):
return x, y, z
return bar
c = foo().func_closure
print [i.cell_contents for i in c]
Salida:
>>>
[''I am free'', ''I am free too'']
A medida que foo
está devolviendo la bar
funciones que está usando su propio valor x
, pero no y
o z
. Por lo tanto, vienen bajo __closure__
.
Beazley pg 100 menciona:
>>>python.__closure__
(<cell at 0x67f50: str object at 0x69230>,)
>>>python.__closure__[0].cell_contents
Mi entendimiento es que __closure__
es una lista, pero ¿qué es todo este objeto de celda y objeto str? ¿Eso parece una tupla de 1 aría?
Las celdas de cierre se refieren a los valores necesarios para la función, pero se toman del ámbito circundante.
Cuando Python compila una función anidada, anota las variables a las que hace referencia, pero solo están definidas en una función primaria (no global) en los objetos de código tanto para la función anidada como para el ámbito principal. Estos son los co_freevars
y co_cellvars
en los objetos __code__
de estas funciones, respectivamente.
Luego, cuando realmente crea la función anidada (lo que sucede cuando se ejecuta la función principal), esas referencias se utilizan para adjuntar un cierre a la función anidada.
El cierre de una función contiene una tupla de celdas, una para cada variable libre (nombrada en co_freevars
); las celdas son referencias especiales a variables locales de un ámbito principal, que siguen los valores a los que apuntan esas variables locales. Esto se ilustra mejor con un ejemplo:
def foo():
def bar():
print(spam)
spam = ''ham''
bar()
spam = ''eggs''
bar()
return bar
b = foo()
b()
En el ejemplo anterior, la bar
funciones tiene una celda de cierre, que apunta a spam
en la función foo
. La celda sigue el valor del spam
. Más importante aún, una vez que se completa foo()
y se devuelve la bar
, la celda continúa haciendo referencia al valor (la cadena de eggs
) aunque ya no existe la variable spam
dentro de foo
.
Por lo tanto, el código anterior genera:
>>> b=foo()
ham
eggs
>>> b()
eggs
y b.__closure__[0].cell_contents
es ''eggs''
.
Tenga en cuenta que el cierre se anula cuando se llama a la bar()
; El cierre no captura el valor aquí. Eso hace una diferencia cuando produce funciones anidadas (con expresiones lambda
o declaraciones def
) que hacen referencia a la variable de bucle:
def foo():
bar = []
for spam in (''ham'', ''eggs'', ''salad''):
bar.append(lambda: spam)
return bar
for bar in foo():
print bar()
Lo anterior imprimirá la salad
tres veces seguidas, porque las tres funciones lambda
referencia a la variable de spam
, no al valor al que estaba vinculado cuando se creó el objeto de función. En el momento for
finaliza el bucle for
, el spam
estaba vinculado a la ''salad''
, por lo que los tres cierres se resolverán con ese valor.
cuando se define una función anidada ( closure
) en python:
la función externa utiliza co_cellvars
para anotar las variables definidas en la función externa que podrían ser referenciadas por la función interna.
la función interna usa co_freevars
para anotar las variables definidas en la función externa, a las que se podría hacer referencia para su uso posterior.
Ejemplo:
# python3
Python 3.4.5 (default, May 29 2017, 15:17:55)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo(n):
... a = 1
... def g(n):
... return a - n
... return g
...
>>> foo.__closure__
>>> foo.__code__.co_freevars
()
>>> foo.__code__.co_cellvars
(''a'',)
>>> foo(0).__closure__
(<cell at 0x7f2cd98db1c8: int object at 0x7f2cd9847960>,)
>>> foo(0).__closure__[0].cell_contents
1
>>> foo(0).__code__.co_freevars
(''a'',)
>>> foo(0).__code__.co_cellvars
()
>>> def f():
... a = "HELO"
... b = 1.0
... def w(c):
... return a,b,c
... return w
>>> w = f()
>>> w.__closure__
(<cell at 0xa05c4ac: str object at 0x9e91b74>, <cell at 0xa05c3bc: float object at 0xb733dde8>)
>>> w.__closure__[0].cell_contents
''HELO''
>>> w.__closure__[1].cell_contents
1.0
Nunca he visto el tipo de celular usado en ningún otro lugar. Parece ser construido específicamente para contener variables de cierre.