So recently I understand the concept of function closure.
def outer():
somevar = []
assert \"somevar\" in locals() and not \"somevar\" in globals()
This depends on the python implementation. I assume you mean CPython.
The __code__
(or func_code
) has a co_freevars
attribute that contains the name of all non-local variables (they are called "free vars" as if a python function was a logical formula where the arguments and local variables are quantified variables)
From these various attribute you can obtain a mapping from local and non-local names to cells.
In [35]: function.__code__.co_freevars
Out[35]: ('somevar',)
The co_varnames
attribute lists all locally define names:
In [36]: function.__code__.co_varnames
Out[36]: ()
In [37]: def outer():
...: somevar = ["stackoverflow"]
...: def inner():
...: x = 1
...: somevar.append(5)
...: return somevar
...: return inner
...:
...: function = outer()
In [38]: function.__code__.co_varnames
Out[38]: ('x',)
While co_cellvars
says which local names are used by inner functions:
In [43]: outer.__code__.co_cellvars
Out[43]: ('somevar',)
All closure functions have __closure__
attribute. This attribute returns a tuple of cell objects. And The cell object has cell_contents
attribute which stores the value of variable.
In [44]: function.__closure__
Out[44]: (<cell at 0x7f4e06b002b8: list object at 0x7f4e06b522c8>,)
In [45]: function.__closure__[0].cell_contents
Out[45]: ["stackoverflow"]
Luckily for us, functions in Python are first class objects. So we can gain a little more understanding of the closures by inspecting the function objects. And all Python functions have a closure attribute that lets us examine the enclosing variables associated with a closure function.
>>> def f():
... pass
...
>>> repr(f); repr(f.__closure__)
'<function f at 0x0153A330>'
'None'