如何检测函数是不是已在本地定义?

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 看到该函数是在本地空间中定义的(打印文本中的&lt;locals&gt;),但我看不出如何找到那部分数据。我浏览了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 属性来区分barbaz @ 对象(它是一个封闭的名称元组,所以如果它是空的,那么它不是一个闭包,尽管它可能仍然是一个嵌套函数):

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

以上是关于如何检测函数是不是已在本地定义?的主要内容,如果未能解决你的问题,请参考以下文章

如何检测是不是已在android中访问过联系人

如何在 R 中引用函数中的本地环境?

我们是不是有任何通用函数来检查页面是不是已在 Selenium 中完全加载

ORA-00937: 不是单组分组函数,已在使用 group by

骨干本地存储“未定义不是函数”

如何强制我的本地 Azure 函数服务器使用 HTTP 2.0 而不是 1.1?