Reflect / Inspect closed-over variables in Python

2020-02-05 08:55发布

If I have:

def f(x):
  def g(y):
    return x + y
  return g

f2 = f(2)

Is there a way to find the x binding that f2 will use? I looked at inspect but could not tell if some of the frame stuff would apply. In other words, could I define a closed_vars() below:

def closed_vars(anF):
    ... return ...

assert closedVars(f2) == {'x': 2}

3条回答
混吃等死
2楼-- · 2020-02-05 09:24

You can get the cell contents by checking out f.func_closure (works in Python 2.7.5):

>>> def f(x):
...   def g(y):
...     return x + y
...   return g
... 
>>> f2 = f(2)
>>> [cell.cell_contents for cell in f2.func_closure]
[2]

Python 3.3 has an inspect.getclosurevars function:

Get the mapping of external name references in a Python function or method func to their current values. A named tuple ClosureVars(nonlocals, globals, builtins, unbound) is returned. nonlocals maps referenced names to lexical closure variables, globals to the function’s module globals and builtins to the builtins visible from the function body. unbound is the set of names referenced in the function that could not be resolved at all given the current module globals and builtins.

I'm not yet sure if you can get the closed-over variable names pre-Python 3.3.

查看更多
Root(大扎)
3楼-- · 2020-02-05 09:24

Python 3 Update - Feb 2019

Purposely, writing this out long-hand:

def f(x):
    def g(y):
        return x + y
    return g

f2 = f(2)

def closedVars(anF):
    keys = f2.__code__.co_freevars
    values = [cell.cell_contents for cell in f2.__closure__]
    # keys and values definitely match in correct order in general case?
    return dict(zip(keys, values))

assert closedVars(f2) == {'x': 2}

What I am less clear on, and this applies to the answer marked correct above also, is whether the ordering between the tuple of (non-local closure) variable names (__code__.co_freevars) and the ordering of the variable values (f2.__closure__) are guaranteed to match (which the zip operation depends upon). In the simple example used to ask the question, we are only dealing with a single variable x so the above would suffice for that specific case.

Be good if anyone can confirm the general case rather than just assume it to be so?

查看更多
兄弟一词,经得起流年.
4楼-- · 2020-02-05 09:35

You don't have to use the inspect module here.

>>> dict(zip(f2.func_code.co_freevars, (c.cell_contents for c in f2.func_closure)))
{'x': 2}

works in Python 2.7

查看更多
登录 后发表回答