如何检测函数是不是已在本地定义?
Posted
技术标签:
【中文标题】如何检测函数是不是已在本地定义?【英文标题】:How to detect if a function has been defined locally?如何检测函数是否已在本地定义? 【发布时间】:2018-03-10 19:45:07 【问题描述】:在 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
模块,没有看到任何类似的东西。
我不能依赖函数是否在全局变量中。
我用什么?
【问题讨论】:
【参考方案1】:首先,直接的方法是检查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
您可以通过测试__closure__
属性(如果没有关闭嵌套变量,则为None
)或检查@987654334 的co_freevars
属性来区分bar
和baz
@ 对象(它是一个封闭的名称元组,所以如果它是空的,那么它不是一个闭包,尽管它可能仍然是一个嵌套函数):
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)
【讨论】:
【参考方案2】:好吧,这是一个 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
【讨论】:
我认为这是一个合法的好解决方案。__qualname__
也是用于创建函数的 repr
的(至少在 CPython 中),它没有检查其他隐藏属性。
@JimFasarakisHilliard 是的,我在玩 inspect.currentframe().f_back.f_locals is globals()
,这似乎也有效,但我更喜欢 less。以上是关于如何检测函数是不是已在本地定义?的主要内容,如果未能解决你的问题,请参考以下文章
我们是不是有任何通用函数来检查页面是不是已在 Selenium 中完全加载