如何区分装饰器中的方法和函数? [英] How to differentiate between method and function in a decorator?

查看:65
本文介绍了如何区分装饰器中的方法和函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个装饰器,该装饰器的作用取决于是否应用于函数或方法。

I want to write a decorator that acts differently depending on whether it is applied to a function or to a method.

def some_decorator(func):
    if the_magic_happens_here(func): # <---- Point of interest
        print 'Yay, found a method ^_^ (unbound jet)'
    else:
        print 'Meh, just an ordinary function :/'
    return func

class MyClass(object):
    @some_decorator
    def method(self):
        pass

@some_decorator
def function():
    pass

我尝试了 inspect.ismethod() inspect.ismethoddescriptor() inspect.isfunction()但没有运气。问题在于,方法实际上既不是绑定方法也不是非绑定方法,而是普通函数,只要从类主体内部进行访问即可。

I tried inspect.ismethod(), inspect.ismethoddescriptor() and inspect.isfunction() but no luck. The problem is that a method actually is neither a bound nor an unbound method but an ordinary function as long as it is accessed from within the class body.

我真正想要的是要做的是将装饰器的动作延迟到实际实例化该类的位置,因为我需要方法在其实例范围内可调用。为此,我想用属性标记方法,然后在 MyClass .__ new __()方法时搜索这些属性。 c>被调用。该装饰器应工作的类需要从我控制的类中继承。您可以使用该事实作为解决方案。

What I really want to do is to delay the actions of the decorator to the point the class is actually instantiated because I need the methods to be callable in their instance scope. For this, I want to mark methods with an attribute and later search for these attributes when the .__new__() method of MyClass is called. The classes for which this decorator should work are required to inherit from a class that is under my control. You can use that fact for your solution.

对于正常功能,不需要延迟,装饰器应立即采取行动。这就是为什么我要区分这两种情况。

In the case of a normal function the delay is not necessary and the decorator should take action immediately. That is why I wand to differentiate these two cases.

推荐答案

我将依赖于约定,即将成为方法的函数具有一个第一个参数名为 self ,而其他函数则没有。易碎,但是然后,没有真正可靠的方法。

I would rely on the convention that functions that will become methods have a first argument named self, and other functions don't. Fragile, but then, there's no really solid way.

所以(伪代码,因为我有评论来代替这两种情况下您想做什么...):

So (pseudocode as I have comments in lieu of what you want to do in either case...):

import inspect
import functools

def decorator(f):
  args = inspect.getargspec(f)
  if args and args[0] == 'self':
     # looks like a (future) method...
  else:
     # looks like a "real" function
     @functools.wraps(f)
     def wrapper  # etc etc

一种使它更加坚实的方法,正如您所说的,所有涉及的类都从您控制的类中继承,是让该类提供一个元类(当然也将由所述类继承)它会在课程正文末尾检查内容。使包装的功能可访问,例如通过 wrapper._f = f 和元类的 __ init __ 可以检查所有包装方法是否确实具有 self 作为第一个参数。

One way to make it a bit more solid, as you say all classes involved inherit from a class under your control, is to have that class provide a metaclass (which will also of course be inherited by said classes) which checks things at the end of the class body. Make the wrapped function accessible e.g. by wrapper._f = f and the metaclass's __init__ can check that all wrapped methods did indeed have self as the first argument.

不幸的是,没有简单的方法来检查 other 功能(非未来) -方法)没有这样的第一个参数,因为在这种情况下您无法控制环境。装饰者可以通过 f_globals检查顶级功能(那些 def 是其模块中的顶级语句的功能)(全局字典,即模块的字典)和函数的 f_name 属性-如果函数具有这样的全局性,那么以后就不会将其分配为类的一个属性(因此仍然成为将来的方法;-),因此名为$ arg的 self (如果存在)可以被诊断为错误并发出警告(尽管仍然将函数视为实函数;-)。

Unfortunately there's no easy way to check that other functions (non-future-methods) being wrapped didn't have such a first argument, since you're not in control of the environment in that case. The decorator might check for "top-level" functions (ones whose def is a top-level statement in their module), via the f_globals (globals dict, i.e., module's dict) and f_name attributes of the function -- if the function's such a global presumably it won't later be assigned as an attribute of the class (thereby becoming a future-method anyway;-) so the self named first arg, if there, can be diagnosed as wrong and warned about (while still treating the function as a real function;-).

一种选择是在有实函数的假设下,在装饰器本身中进行装饰,但也要使其可用原始函数对象为 wrapper._f 。然后,元类的 __ init __ 可以重新修饰类主体中所有已标记为这种样式的函数。这种方法比我刚才概述的遵循惯例的方法要牢固得多,即使有额外的检查也是如此。还是像这样

One alternative would be to do the decoration in the decorator itself under the hypothesis of a real function, but also make available the original function object as wrapper._f. Then, the metaclass's __init__ can re-do the decoration for all functions in the class body that it sees have been marked this way. This approach is much more solid than the convention-relying one I just sketched, even with the extra checks. Still, something like

class Foo(Bar): ... # no decorations

@decorator
def f(*a, **k): ...

Foo.f = f   # "a killer"... function becomes method!

仍然会有问题-您可以尝试使用 __ setattr __ 在您的元类中(但是 class 语句之后的其他对类属性的分配可能会出现问题)。

would still be problematic -- you could try intercepting this with a __setattr__ in your metaclass (but then other assignments to class attributes after the class statement can become problematic).

用户的代码拥有做时髦事情的自由越多(Python通常给程序员带来很多这样的自由),当然,框架Y代码很难将事情严格控制。 -)。

The more the user's code has freedom to do funky things (and Python generally leaves the programmer a lot of such freedom), the harder time your "framework-y" code has keeping things under tight control instead, of course;-).

这篇关于如何区分装饰器中的方法和函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆