cul*_*rón 8 python python-3.x python-decorators
在Python中,我有一个装饰器,如果函数在调用它的本地定义,则必须跳过任何实际工作.我做了一个简单的测试脚本:
def fn1():
# @my_decorator will be here
def fn2():
pass
print(fn2)
return fn2
x = fn1()
print(x)
print(x.__module__)
Run Code Online (Sandbox Code Playgroud)
它打印这个:
<function fn1.<locals>.fn2 at 0x7fd61bdf3ae8>
<function fn1.<locals>.fn2 at 0x7fd61bdf3ae8>
__main__
Run Code Online (Sandbox Code Playgroud)
正如我所看到的,Python看到函数是在局部空间中定义的(<locals>在打印文本中),但我看不出如何找到那些数据.我走过inspect模块,看不到任何类似的东西.
我不能依赖函数是否在全局变量中.
我该用什么?
首先,直接的方法是检查函数的代码对象上是否设置了标志CO_NESTED:
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 ...
Run Code Online (Sandbox Code Playgroud)
也就是说,如果您关心的是您是否真正关闭了任何东西,还有另一种方法。不使用封闭作用域中任何内容的函数是嵌套的,但不是闭包,这种区别通常很重要。例如:
def foo(x):
def bar(y):
pass
return bar
Run Code Online (Sandbox Code Playgroud)
没有创建闭包,因为没有bar使用调用范围内的变量foo。相比之下,即使它是一个垃圾引用,这也只是通过从封闭范围中读取 的值来创建一个闭包:x
def foo(x):
def baz(y):
x
return baz
Run Code Online (Sandbox Code Playgroud)
您可以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)
Run Code Online (Sandbox Code Playgroud)
好吧,这是一个 hacky 方法:
'<locals>' in f.__qualname__
Run Code Online (Sandbox Code Playgroud)
但对我来说它似乎很脆弱。
另一种方法是使用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
Run Code Online (Sandbox Code Playgroud)