This code in Python 2.7 creates a closure around func, enclosing the par variable:
def creator(par):
def func(arg):
return par + arg
return func
It can by used like so:
f = creator(7)
f(3) # returns 10
At runtime, is there a way to get name of the function in which the closure was defined? That is: having only access to the f variable, can I get information that f closure was defined inside creator function?
I am using Python 2.7.
I suspect it can't be done in Python 2.7, at least not directly. Here's the pretty-printed contents of your function
f, minus some obviously-unrelated entries:The only interesting keys are
func_closure(whichis __closure__) andfunc_code(whichis __code__), but neither help.The closure is a tuple of
cellobjects, each of which contains the value of a key-value pair in the closed-over environment. There is only one value inf.func_closure, that of theparvariable:A
celldoes not contain a reference to the creator of the closure, the function using the closure, or even the enclosed environment itself. (Elements of the enclosed environment appear to be retrieved based on their position in the tuple ofcellobjects.)The function code object comes closer, but also doesn't name its creator. Minus the obviously irrelevant entries, it contains:
This contains the name of the closed-over variable (
'co_freevars': ('par',),), and the name of the function insidecreator('co_name': 'func',) but not the name or any reference to the outer function.Partial Solution
There is a way to identify the outer function of an enclosed function, if you have a reference to both. The creating function's function code object will contain a reference to the closed-over function's code object:
You can determine that
creatoris the source off, because the tuplecreator.func_code.co_constscontains a reference tof.func_code:The same code object is used by every function returned by
creator(their differences stored incellobjects, not code objects):So, if you can narrow the potential sources down to say, values in
globals()or indir(some_class), you can test each of them to see if it's the "parent" off:This kind of sucks, really, because it doesn't point you to the creator, it just identifies the creator if you've already found it. Also, it can be fooled if someone uses
creator.__code__to build an imposter:Questionable Improvements
There are other clues, once a potential creator is found, but they don't really constitute proof. Examples include:
The name of the closed-over variable
'par'appears in bothfas a free variable, and increatoras a "cell" variable:The name of
f(which is'func', not'f') appears in the creator's function code object. (Function code objects are immutable, sof.func_code.co_namemust be the original name assigned tofwhen it was created.f.__name__could have been reassigned since then. So couldf.func_code--- the whole code object --- but that's nowhere near as common.)Because function definitions can be deeply nested --- meaning different free variables in the innermost function could be defined (recorded in
co_cellvars) in different outer functions --- I don't think adding checks for these would makeis_creatorany "smarter".