5

[更新]:回答以下问题

我有一个检查程序,一个目标是让装饰器中的逻辑知道它正在装饰的函数是类方法还是常规函数。这以一种奇怪的方式失败了。以下是在 Python 2.6 中运行的代码:

def decorate(f):
    print 'decorator thinks function is', f
    return f

class Test(object):
    @decorate
    def test_call(self):
        pass

if __name__ == '__main__':
    Test().test_call()
    print 'main thinks function is', Test().test_call

然后在执行时:

decorator thinks function is <function test_call at 0x10041cd70>
main thinks function is <bound method Test.test_call of <__main__.Test object at 0x100425a90>>

关于出了什么问题的任何线索,以及@decorate 是否有可能正确推断出 test_call 是一种方法?

[答案] 下面卡尔的回答几乎是完美的。在子类调用的方法上使用装饰器时遇到问题。我修改了他的代码以包含对超类成员的 im_func 比较:

ismethod = False
for item in inspect.getmro(type(args[0])):
    for x in inspect.getmembers(item):
        if 'im_func' in dir(x[1]):
            ismethod = x[1].im_func == newf
            if ismethod:
                break
    else:
        continue
    break
4

4 回答 4

5

正如其他人所说,函数在绑定之前已被修饰,因此您无法直接确定它是“方法”还是“函数”。

确定函数是否为方法的合理方法是检查“self”是否为第一个参数。虽然并非万无一失,但大多数 Python 代码都遵循此约定:

import inspect
ismethod = inspect.getargspec(method).args[0] == 'self'

这是一种复杂的方式,似乎可以自动确定该方法是否是绑定的。适用于 CPython 2.6 上的一些简单案例,但没有承诺。如果第一个参数是绑定了修饰函数的对象,则它确定函数是方法。

import inspect

def decorate(f):
    def detect(*args, **kwargs):
        try:
            members = inspect.getmembers(args[0])
            members = (x[1].im_func for x in members if 'im_func' in dir(x[1]))
            ismethod = detect in members
        except:
            ismethod = False
        print ismethod

        return f(*args, **kwargs)
    return detect

@decorate
def foo():
    pass

class bar(object):
    @decorate
    def baz(self):
        pass

foo() # prints False
bar().baz() # prints True
于 2010-08-25T08:35:32.960 回答
3

不,这是不可能的,因为绑定的方法和函数之间没有内在的区别。方法只是一个封装起来的函数,以获取调用实例作为第一个参数(使用 Python描述符)。

像这样的调用:

Test.test_call

它返回一个未绑定的方法,转换为

Test.__dict__[ 'test_call' ].__get__( None, spam )

这是一个未绑定的方法,即使

Test.__dict__[ 'test_call' ]

是一个函数。这是因为函数是其__get__方法返回方法的描述符;当 Python 在查找链中看到其中之一时,它会调用该__get__方法而不是继续查找链。

实际上,函数的“绑定方法”是在运行时确定的,而不是在定义时确定的!

装饰器只是看到定义的函数,而不是在 a 中查找它__dict__,因此无法判断它是否正在查看绑定方法。


可以使用修改 的类装饰器来做到这一点__getattribute__但这是一个特别讨厌的 hack。为什么一定要有这个功能?当然,既然你必须自己把装饰器放在函数上,你可以给它传递一个参数,说明所述函数是否定义在一个类中?

class Test:
    @decorate( method = True )
    def test_call:
        ...

@decorate( method = False )
def test_call:
    ...
于 2010-08-25T08:44:09.680 回答
1

您的装饰器在函数成为方法之前运行。def类中的关键字在任何其他地方定义了一个函数行,然后将类主体中定义的函数作为方法添加到类中。装饰器在函数被类处理之前对其进行操作,这就是你的代码“失败”的原因。

@decorate 无法看到该函数实际上是一个方法。一种解决方法是对函数进行任何修饰(例如添加属性do_something_about_me_if_I_am_a_method ;-)),然后在计算类后再次处理它(迭代类成员并对修饰的那些成员做任何你想做的事情)。

于 2010-08-25T08:30:39.190 回答
0

我尝试了一个稍微不同的例子,一种修饰方法和一种未修饰方法。

def decorate(f):
  print 'decorator thinks function is', f
  return f

class Test(object):
  @decorate
  def test_call(self):
    pass
  def test_call_2(self):
    pass

if __name__ == '__main__':
  print 'main thinks function is', Test.test_call
  print 'main thinks function 2 is', Test.test_call_2

然后输出是:

decorator thinks function is <function test_call at 0x100426b18>
main thinks function is <unbound method Test.test_call>
main thinks function 2 is <unbound method Test.test_call_2>

因此,装饰器看到了与主函数不同的类型,但装饰器并没有改变函数的类型,否则它将与未装饰的函数不同。

于 2010-08-25T09:01:53.930 回答