How to differentiate between method and function in a decorator?
我想编写一个根据应用于函数还是方法而不同的修饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
我试过
我真正想做的是将修饰器的操作延迟到类实际被实例化的程度,因为我需要在它们的实例范围内调用这些方法。为此,我想用一个属性标记方法,然后在调用
在正常功能的情况下,无需延迟,装饰师应立即采取措施。这就是为什么我想区分这两种情况。
我将依赖于这样的约定:将成为方法的函数有一个名为
所以(伪代码,因为我有注释来代替您在任何情况下想做的事情…):
1 2 3 4 5 6 7 8 9 10 11 | 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 |
有一种方法可以让它更坚固一点,正如您所说,所有涉及的类都继承自您控制的类,那就是让该类提供一个元类(当然,元类也将由所述类继承),它检查类体末尾的内容。使被包装的函数可访问,例如由
不幸的是,没有一种简单的方法可以检查被包装的其他函数(非未来方法)是否没有这样的第一个参数,因为在这种情况下,您无法控制环境。修饰程序可以通过函数的
另一种选择是在实际功能的假设下在装饰器本身中进行装饰,但也使原始功能对象作为
1 2 3 4 5 6 | class Foo(Bar): ... # no decorations @decorator def f(*a, **k): ... Foo.f = f #"a killer"... function becomes method! |
仍然是有问题的——您可以尝试在元类中使用
用户的代码有更多的自由去做一些奇怪的事情(而python通常会给程序员留下很多这样的自由),你的"framework-y"代码很难让事情保持在严格的控制之下,当然;-。
您是否需要在选择返回哪个包装器的地方发生魔力,或者您是否可以将魔力推迟到实际调用函数为止?您总是可以尝试使用装饰器的参数来指示它应该使用两个包装器中的哪一个,比如
1 2 3 4 5 6 7 8 9 10 11 | def some_decorator( clams ): def _mydecor(func ): @wraps(func) def wrapping(*args....) ... return wrapping def _myclassdecor(func): @wraps(func) ..... return _mydecor if clams else _myclassdecor |
我可能建议的另一件事是创建一个元类,并在元类中定义init方法,以查找用修饰器修饰的方法,并相应地修改它们,就像亚历克斯暗示的那样。将这个元类与您的基类一起使用,并且由于将使用decorator的所有类都将从基类继承,因此它们还将获取元类类型并使用其init。
从python 3.3开始,使用pep 3155:
1 2 3 4 5 6 | def some_decorator(func): if func.__name__ != func.__qualname__: print 'Yay, found a method ^_^ (unbound jet)' else: print 'Meh, just an ordinary function :/' return func |
方法
您只需要检查正在修饰的函数是否具有
请注意,下面的代码示例在调用时进行检测,但您也可以在装饰时进行检测。只需将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) [GCC 4.4.1] on linux2 Type"help","copyright","credits" or"license" for more information. >>> def deco(f): ... def _wrapper(*args, **kwargs): ... if hasattr(f, 'im_func'): ... print 'method' ... else: ... print 'function' ... return _wrapper ... >>> deco(lambda x: None)() function >>> def f(x): ... return x + 5 ... >>> deco(f)() function >>> class A: ... def f(self, x): ... return x + 5 ... >>> a = A() >>> deco(a.f)() method >>> deco(A.f)() method >>> |
编辑
哦,快点!我完全错了。我应该更仔细地阅读亚历克斯的帖子。
1 2 3 4 5 6 7 8 | >>> class B: ... @deco ... def f(self, x): ... return x +5 ... >>> b = B() >>> b.f() function |