Python:装饰器可以确定函数是否在类中定义吗?

发布于 2024-12-26 04:44:07 字数 895 浏览 5 评论 0原文

我正在编写一个装饰器,出于各种烦人的原因[0],检查它所包装的函数是独立定义还是作为类的一部分定义(以及新类是哪些类的子类)是很方便的。

例如:

def my_decorator(f):
    defined_in_class = ??
    print "%r: %s" %(f, defined_in_class)

@my_decorator
def foo(): pass

class Bar(object):
    @my_decorator
    def bar(self): pass

应该打印:

<function foo …>: False
<function bar …>: True

另外,请注意:

  • 在应用装饰器时,该函数仍然是一个函数,而不是未绑定的方法,因此测试实例/未绑定的方法(使用 typeof检查)将不起作用。
  • 请仅提供解决这个问题的建议 - 我知道有很多类似的方法可以实现这一目标(例如,使用类装饰器),但我希望它们发生在装修时间,不晚。

[0]:具体来说,我正在编写一个装饰器,可以轻松使用 nose 进行参数化测试。但是,nose 不会在unittest.TestCase 的子类上运行测试生成器,因此我希望我的装饰器能够确定它是否正在运行在 TestCase 的子类中使用并失败并出现适当的错误。显而易见的解决方案 - 在调用包装函数之前使用 isinstance(self, TestCase) 不起作用,因为包装函数需要成为一个生成器,而这不能得到完全执行

I'm writing a decorator, and for various annoying reasons[0] it would be expedient to check if the function it is wrapping is being defined stand-alone or as part of a class (and further which classes that new class is subclassing).

For example:

def my_decorator(f):
    defined_in_class = ??
    print "%r: %s" %(f, defined_in_class)

@my_decorator
def foo(): pass

class Bar(object):
    @my_decorator
    def bar(self): pass

Should print:

<function foo …>: False
<function bar …>: True

Also, please note:

  • At the point decorators are applied the function will still be a function, not an unbound method, so testing for instance/unbound method (using typeof or inspect) will not work.
  • Please only offer suggestions that solve this problem — I'm aware that there are many similar ways to accomplish this end (ex, using a class decorator), but I would like them to happen at decoration time, not later.

[0]: specifically, I'm writing a decorator that will make it easy to do parameterized testing with nose. However, nose will not run test generators on subclasses of unittest.TestCase, so I would like my decorator to be able to determine if it's being used inside a subclass of TestCase and fail with an appropriate error. The obvious solution - using isinstance(self, TestCase) before calling the wrapped function doesn't work, because the wrapped function needs to be a generator, which doesn't get executed at all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

芸娘子的小脾气 2025-01-02 04:44:07

包装方法时,请查看 inspect.stack() 的输出。当装饰器执行时,当前堆栈帧是对装饰器的函数调用;下一个堆栈帧是应用于新方法的 @ 包装操作;第三个框架将是类定义本身,它需要一个单独的堆栈框架,因为类定义是它自己的名称空间(在执行完成时被包装以创建一个类)。

因此,我建议:

defined_in_class = (len(frames) > 2 and
                    frames[2][4][0].strip().startswith('class '))

如果所有这些疯狂的索引看起来无法维护,那么您可以通过将框架一块一块地拆开来更明确,如下所示:

import inspect
frames = inspect.stack()
defined_in_class = False
if len(frames) > 2:
    maybe_class_frame = frames[2]
    statement_list = maybe_class_frame[4]
    first_statment = statement_list[0]
    if first_statment.strip().startswith('class '):
        defined_in_class = True

请注意,我没有看到任何询问Python的方法关于包装器运行时的类名或继承层次结构;该点在处理步骤中“为时过早”,因为类创建尚未完成。要么自己解析以 class 开头的行,然后查看该框架的全局变量以查找超类,要么浏览 frames[1] 代码对象以查看您想要的内容可以学习 - 上面代码中的类名似乎是 frames[1][0].f_code.co_name ,但是我找不到任何方法来了解当类创建完成。

Take a look at the output of inspect.stack() when you wrap a method. When your decorator's execution is underway, the current stack frame is the function call to your decorator; the next stack frame down is the @ wrapping action that is being applied to the new method; and the third frame will be the class definition itself, which merits a separate stack frame because the class definition is its own namespace (that is wrapped up to create a class when it is done executing).

I suggest, therefore:

defined_in_class = (len(frames) > 2 and
                    frames[2][4][0].strip().startswith('class '))

If all of those crazy indexes look unmaintainable, then you can be more explicit by taking the frame apart piece by piece, like this:

import inspect
frames = inspect.stack()
defined_in_class = False
if len(frames) > 2:
    maybe_class_frame = frames[2]
    statement_list = maybe_class_frame[4]
    first_statment = statement_list[0]
    if first_statment.strip().startswith('class '):
        defined_in_class = True

Note that I do not see any way to ask Python about the class name or inheritance hierarchy at the moment your wrapper runs; that point is "too early" in the processing steps, since the class creation is not yet finished. Either parse the line that begins with class yourself and then look in that frame's globals to find the superclass, or else poke around the frames[1] code object to see what you can learn — it appears that the class name winds up being frames[1][0].f_code.co_name in the above code, but I cannot find any way to learn what superclasses will be attached when the class creation finishes up.

魂归处 2025-01-02 04:44:07

虽然有点晚了,但这已被证明是确定装饰器是否正在类中定义的函数上使用的可靠方法:

frames = inspect.stack()

className = None
for frame in frames[1:]:
    if frame[3] == "<module>":
        # At module level, go no further
        break
    elif '__module__' in frame[0].f_code.co_names:
        className = frame[0].f_code.co_name
        break

与已接受的答案相比,此方法的优点是它可以与例如 py2exe 一起使用。

A little late to the party here, but this has proven to be a reliable means of determining if a decorator is being used on a function defined in a class:

frames = inspect.stack()

className = None
for frame in frames[1:]:
    if frame[3] == "<module>":
        # At module level, go no further
        break
    elif '__module__' in frame[0].f_code.co_names:
        className = frame[0].f_code.co_name
        break

The advantage of this method over the accepted answer is that it works with e.g. py2exe.

假情假意假温柔 2025-01-02 04:44:07

我得到了一些 hacky 解决方案:

import inspect

def my_decorator(f):
    args = inspect.getargspec(f).args
    defined_in_class = bool(args and args[0] == 'self')
    print "%r: %s" %(f, defined_in_class)

但它依赖于函数中 self 参数的存在。

Some hacky solution that I've got:

import inspect

def my_decorator(f):
    args = inspect.getargspec(f).args
    defined_in_class = bool(args and args[0] == 'self')
    print "%r: %s" %(f, defined_in_class)

But it relays on the presence of self argument in function.

回忆追雨的时光 2025-01-02 04:44:07

您可以使用包 wrapt 来检查
- 实例/类方法
- 课程
- 独立函数/静态方法:

参见 wrapt 的项目页面:https:// pypi.org/project/wrapt/

you can use the package wrapt to check for
- instance/class methods
- classes
- freestanding functions/static methods:

See the project page of wrapt: https://pypi.org/project/wrapt/

小瓶盖 2025-01-02 04:44:07

您可以检查装饰器本身是否在模块级别调用或嵌套在其他内容中。

defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>"

You could check if the decorator itself is being called at the module level or nested within something else.

defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>"
め可乐爱微笑 2025-01-02 04:44:07

我认为 inspect 模块中的函数将执行您想要的操作,特别是 isfunctionismethod

>>> import inspect
>>> def foo(): pass
... 
>>> inspect.isfunction(foo)
True
>>> inspect.ismethod(foo)
False
>>> class C(object):
...     def foo(self):
...             pass
... 
>>> inspect.isfunction(C.foo)
False
>>> inspect.ismethod(C.foo)
True
>>> inspect.isfunction(C().foo)
False
>>> inspect.ismethod(C().foo)
True

然后您可以按照类型和成员表 访问绑定或未绑定方法内的函数:

>>> C.foo.im_func
<function foo at 0x1062dfaa0>
>>> inspect.isfunction(C.foo.im_func)
True
>>> inspect.ismethod(C.foo.im_func)
False

I think the functions in the inspect module will do what you want, particularly isfunction and ismethod:

>>> import inspect
>>> def foo(): pass
... 
>>> inspect.isfunction(foo)
True
>>> inspect.ismethod(foo)
False
>>> class C(object):
...     def foo(self):
...             pass
... 
>>> inspect.isfunction(C.foo)
False
>>> inspect.ismethod(C.foo)
True
>>> inspect.isfunction(C().foo)
False
>>> inspect.ismethod(C().foo)
True

You can then follow the Types and Members table to access the function inside the bound or unbound method:

>>> C.foo.im_func
<function foo at 0x1062dfaa0>
>>> inspect.isfunction(C.foo.im_func)
True
>>> inspect.ismethod(C.foo.im_func)
False
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文