当你想在你的代码中找到一个错误时,这很难;当你认为你的代码是不会有错误时,这就更难了。

python-decorator将函数状态从方法更改为函数

admin 103℃
[更新]:在线回答以下问题

我有一个检查程序,目标之一是让decorator中的逻辑知道它正在装饰的函数是类方法还是正则函数。这是以一种奇怪的方式失败。下面是在Python2.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_调用是一个方法,那么有什么线索可以告诉我们出了什么问题吗?

[答:] 卡尔下面的回答近乎完美。我在对子类调用的方法使用decorator时遇到问题。我修改了他的代码,在超类成员上包含一个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

正如其他人所说,函数在被绑定之前就被修饰了,因此您不能直接确定它是'方法'还是'函数'。

判断函数是否是方法的一个合理方法是检查'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

不,这是不可能的,因为绑定方法和函数之间没有内在的区别。方法只是一个包装好的函数,它将调用实例作为第一个参数(使用pythondescriptors)。

类似这样的电话:

Test.test_call

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

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

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

Test.__dict__[ 'test_call' ]

是一个函数。这是因为函数是 __get__ 方法返回方法;当python在查找链中看到其中一个方法时,它调用 __get__ 方法而不是继续向上链。

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

decorator只看到定义的函数,而不在 __dict__ ,因此无法判断它是否正在查看绑定方法。


使用一个修改 __getattribute__ ,但那是一个特别讨厌的黑客。为什么一定要有这个功能?当然,既然您必须自己将decorator放在函数上,那么您可以向它传递一个参数,说明所述函数是否在类中定义?

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

@decorate( method = False )
def test_call:
    ...

装饰程序在函数成为方法之前运行。 def 关键字在类中的任何其他位置定义函数行,然后将类主体中定义的函数作为方法添加到类中。decorator在函数被类处理之前对其进行操作,这就是代码'失败'的原因。

@decorate无法看到函数实际上是一种方法。解决方法是修饰函数,不管它是什么(例如,添加一个属性 do_something_about_me_if_I_am_a_method ;-) )然后在计算完类之后再次处理它(遍历类成员并对那些修饰的成员执行任何您想要的操作)。

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

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>

因此,decorator看到的类型不同于main函数,但decorator没有更改函数的类型,否则它将不同于未修饰的函数。

转载请注明:我的代码 » python-decorator将函数状态从方法更改为函数