How to determine if a function has been defined locally?

In Python, I have a decorator that should skip any real work if the function is defined locally in the one that calls it. I conducted a simple testing script:

def fn1(): # @my_decorator will be here def fn2(): pass print(fn2) return fn2 x = fn1() print(x) print(x.__module__) 

He prints this:

  <function fn1.<locals>.fn2 at 0x7fd61bdf3ae8> <function fn1.<locals>.fn2 at 0x7fd61bdf3ae8> __main__ 

As I see it, Python sees that the function is defined in local space ( <locals> in the printed text), but I don’t see how I can find this bit of data. I went through the inspect module and don't see anything like it.

I cannot rely on whether this function is global or not.

What am i using?

+7
python python-decorators
source share
2 answers

First of all, a direct approach is to check if the CO_NESTED flag is CO_NESTED on the function code object :

 import inspect ... def is_nested(func): return func.__code__.co_flags & inspect.CO_NESTED def deco(func): if is_nested(func): # This is a nested function, return it unchanged return func ... otherwise, do your decoration here ... 

However, there is another approach if you care about whether you really closed something. A function that does not use anything from the enclosing area is nested but not closing, and this distinction is often important. For example:

 def foo(x): def bar(y): pass return bar 

does not make a closure because bar does not use any variables from the scope of the foo call. In contrast, although this is a reference to garbage, it does close simply by reading the x value from the enclosing area:

 def foo(x): def baz(y): pass return bar 

You can determine the difference between bar and baz by testing the __closure__ attribute (which is None if the nested variables have not been closed) or by checking the co_freevars __code__ object attribute (which is a tuple of names closed, so if it is empty, then this is either not a closure , although it can still be a nested function):

 def is_closure(func): return func.__closure__ is not None # Or using documented names, since __closure__ isn't for some reason, # co_freevars is a tuple of names captured from nested scope return bool(func.__code__.co_freevars) # Or on 3.3+, you even get a function to aid you: return bool(inspect.getclosurevars(func).nonlocals) 
+3
source share

Well, here is the hacker approach:

 '<locals>' in f.__qualname__ 

It seems fragile to me.

Another approach is to play with Frame , but I like that even less, I think:

 In [1]: import inspect In [2]: def deco(f): ...: try: ...: frame = inspect.currentframe() ...: print(frame.f_back.f_locals is globals()) ...: finally: ...: del frame ...: return f ...: In [3]: @deco ...: def g(): pass ...: True In [4]: def f(): ...: @deco ...: def g(): pass ...: In [5]: f() False 
+3
source share

All Articles