在Python中,我有一个装饰器,如果函数在调用它的本地定义,则必须跳过任何实际工作。我制作了一个简单的测试脚本:
def fn1():
# @my_decorator will be here
def fn2():
pass
print(fn2)
return fn2
x = fn1()
print(x)
print(x.__module__)
打印出来:
<function fn1.<locals>.fn2 at 0x7fd61bdf3ae8>
<function fn1.<locals>.fn2 at 0x7fd61bdf3ae8>
__main__
正如我所见,Python看到函数是在本地空间中定义的(打印文本中为<locals>
),但我无法看到如何找到该位数据。我走过inspect
模块,看不到任何类似的东西。
我不能依赖函数是否在全局变量中。
我该使用什么?
答案 0 :(得分:4)
嗯,这是一个hacky方法:
'<locals>' in f.__qualname__
但是,对我来说似乎很脆弱。
另一种方法是使用Frame
,但我更喜欢,我认为:
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
答案 1 :(得分:4)
首先,直接的方法是检查是否the CO_NESTED
flag is set on the function's 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 ...
那就是说,如果你关心的是你是否真的关闭过任何东西,还有另一种方法。不使用封闭范围内任何内容的函数是嵌套的,但不是闭包,这种区别通常很重要。例如:
def foo(x):
def bar(y):
pass
return bar
不进行关闭,因为bar
不使用foo
调用范围内的变量。相比之下,即使它是一个垃圾引用,这个 只是通过从封闭范围中读取x
的值来构成一个闭包:
def foo(x):
def baz(y):
x
return baz
您可以通过测试bar
属性(如果没有关闭嵌套变量,则为baz
)或通过检查来确定__closure__
和None
之间的区别co_freevars
对象的__code__
属性(这是一个名字关闭的元组,所以如果它是空的,那么它不是一个闭包,虽然它可能仍然是一个嵌套函数):
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)