我想编写一个装饰器,它的行为会有所不同,具体取决于它是应用于函数还是方法.
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
Run Code Online (Sandbox Code Playgroud)
我想inspect.ismethod(),inspect.ismethoddescriptor()和inspect.isfunction()但没有运气.问题是方法实际上既不是绑定方法也不是非绑定方法,而是普通函数,只要它是从类体内访问即可.
我真正想要做的是将装饰器的动作延迟到实际实例化类的程度,因为我需要在其实例范围内调用方法.为此,我想用属性标记方法,然后在调用.__new__()方法时搜索这些属性MyClass.这个装饰器应该工作的类需要从我控制的类继承.您可以将此事实用于您的解决方案.
在正常功能的情况下,延迟不是必需的,装饰者应该立即采取行动.这就是我想要区分这两种情况的原因.
从 Python 3.3 开始,使用PEP 3155:
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
Run Code Online (Sandbox Code Playgroud)
一种方法,x类的A将有一个__qualname__是A.x同时的函数x将具有__qualname__的x。
我将依赖于惯例,即将成为方法的函数具有名为的第一个参数self,而其他函数则不会.脆弱,但那时,没有真正坚实的方式.
所以(伪代码,因为我有评论代替你想要做的任何一种情况......):
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
Run Code Online (Sandbox Code Playgroud)
一种使它变得更加可靠的方法,正如你所说的所有涉及的类继承自你控制的类,就是让那个类提供一个元类(当然也将由所述类继承),它最后检查一些东西.班级的身体.使包装函数可以访问,例如,wrapper._f = f并且元类__init__可以检查所有包装的方法确实具有self第一个参数.
不幸的是,没有简单的方法来检查被包装的其他函数(非future方法)没有这样的第一个参数,因为在这种情况下你不能控制环境.装饰器可以def通过f_globals(全局字典,即模块的dict)和f_name函数的属性检查"顶级"函数(其模块中是顶层语句的函数) - 如果函数是这样的全局函数的话它不会被分配为类的属性(从而成为一个未来的方法;-)所以self命名的第一个arg,如果有的话,可以被诊断为错误并警告(同时仍然将该函数视为一个真实的功能;-).
一种替代方案是在真实函数的假设下在装饰器本身中进行装饰,但也使原始函数对象可用wrapper._f.然后,元类__init__可以重新对它看到的类体中的所有函数进行装饰.即使有额外的检查,这种方法也比我刚刚描绘的惯用方法更加可靠.还是,像
class Foo(Bar): ... # no decorations
@decorator
def f(*a, **k): ...
Foo.f = f # "a killer"... function becomes method!
Run Code Online (Sandbox Code Playgroud)
仍然会有问题 - 你可以尝试用__setattr__你的元类中的a 来拦截它(但是在class语句出现问题之后再对类属性进行其他赋值).
用户代码可以自由地做出时髦的事情(而Python通常会给程序员带来很多这样的自由),当然,"框架式"代码更难以控制事物,而不是当然;-).