python装饰器出错

时间:2010-04-16 18:45:02

标签: python decorator

我收到此错误

object has no attribute 'im_func'

用这个

class Test(object):
    def __init__(self, f):
        self.func = f

    def __call__( self, *args ):
        return self.func(*args)

pylons代码:

class TestController(BaseController):

    @Test
    def index(self):
        return 'hello world'

完整错误:

File '/env/lib/python2.5/site-packages/WebError-0.10.2-py2.5.egg/weberror/evalexception.py', line 431 in respond
  app_iter = self.application(environ, detect_start_response)
File '/env/lib/python2.5/site-packages/repoze.who-1.0.18-py2.5.egg/repoze/who/middleware.py', line 107 in __call__
  app_iter = app(environ, wrapper.wrap_start_response)
File '/env/lib/python2.5/site-packages/Beaker-1.5.3-py2.5.egg/beaker/middleware.py', line 73 in __call__
  return self.app(environ, start_response)
File '/env/lib/python2.5/site-packages/Beaker-1.5.3-py2.5.egg/beaker/middleware.py', line 152 in __call__
  return self.wrap_app(environ, session_start_response)
File '/env/lib/python2.5/site-packages/Routes-1.10.3-py2.5.egg/routes/middleware.py', line 130 in __call__
  response = self.app(environ, start_response)
File '/env/lib/python2.5/site-packages/Pylons-0.9.7-py2.5.egg/pylons/wsgiapp.py', line 125 in __call__
  response = self.dispatch(controller, environ, start_response)
File '/env/lib/python2.5/site-packages/Pylons-0.9.7-py2.5.egg/pylons/wsgiapp.py', line 324 in dispatch
  return controller(environ, start_response)
File '/project/lib/base.py', line 18 in __call__
  return WSGIController.__call__(self, environ, start_response)
File '/env/lib/python2.5/site-packages/Pylons-0.9.7-py2.5.egg/pylons/controllers/core.py', line 221 in __call__
  response = self._dispatch_call()
File '/env/lib/python2.5/site-packages/Pylons-0.9.7-py2.5.egg/pylons/controllers/core.py', line 172 in _dispatch_call
  response = self._inspect_call(func)
File '/env/lib/python2.5/site-packages/Pylons-0.9.7-py2.5.egg/pylons/controllers/core.py', line 80 in _inspect_call
  argspec = cached_argspecs[func.im_func]
AttributeError: 'Test' object has no attribute 'im_func'

3 个答案:

答案 0 :(得分:4)

TestController.index结束Test的实例,无法访问TestController对象。此外,只有用户定义的方法(必须是函数,而不是对象)才具有im_func属性。您需要实例化Test并让其__call__方法返回一个函数,以便可以传递TestController实例。

class Test(object):
    def __call__( self, f):
        def wrapper(self, *args, **kwargs):
            # anything in the old Test.__call__ goes here.
            return f(self, *args, **kwargs)
        return wrapper

class TestController(BaseController):
    @Test()
    def index(self):
        return 'hello world'

发生了什么

装饰者:

@decorator
def foo(...):

相当于:

def foo(...):
    ...
foo = decorator(foo)

在原始代码中

    @Test
    def index(self):

创建Test的实例并将index传递给构造函数。生成的对象将分配给index的{​​{1}}属性。

TestController
在您尝试致电class TestController(BaseController) def index(self): ... index = Test(index) 之前,不会调用

Test.__call__TestController.index的实例为tcTestController相当于tc.index()tc.index.__call__()

问题是,在Test.__call__(tc.index)的来电中,我们丢失了对Test.__call__的引用。定义tc时不存在,因此无法保存它。此外,看起来Pylons在方法上执行了一些魔术,它期望Test.index是用户定义的方法(具有tc.index属性),而不是对象(它不是)。

我调用的方法在调用im_funcTest.__call__的类型时会发生变化。

TestController.index

class Test(object): def __call__( self, f): # if done properly, __call__ will get invoked when the decorated method # is defined, not when it's invoked print 'Test.__call__' def wrapper(self, *args, **kwargs): # wrapper will get invoked instead of the decorated method print 'wrapper in Test.__call__' return f(self, *args, **kwargs) return wrapper 的定义相当于:

TestController.index

由于class TestController(BaseController): def index(self): ... index = Test()(index) # note: Test.__call__ is invoked here. # 'index' is now 'wrapper' from Test.__call__ tc = TestController tc.index() # wrapper from Test.__call__ is invoked here 是一个函数而不是一个对象,TestController.index等同于tc.index(),我们不会丢失对TestController.index(tc)的引用。

答案 1 :(得分:1)

请参阅http://pylonshq.com/project/pylonshq/ticket/589

当你打电话时,是否有任何monkeypatching或其他奇怪的事情发生? 调用者的完整追溯和来源在这里真的很有帮助。

答案 2 :(得分:1)

要理解为什么这不能按预期工作,您必须了解方法在Python中的工作方式。查找属性时,将调用其__get__方法(如果存在),并使用返回的内容而不是属性本身。其主要用途是实现方法,特殊类型的方法(如类方法),属性等。设置和删除属性有类似的钩子,所有这些都在http://www.python.org/download/releases/2.2.3/descrintro/

上进行了解释

函数已经内置了__get__ magic,因此它们自动作为方法工作,使得绑定方法在查找时传递当前实例。您定义的类不会自动具有此类,因此您必须手动定义它,如下所示:

from functools import partial
class Test(object):
    def __init__(self, f):
        self.func = f

    def __call__(self, *args):
        return self.func(*args)

    def __get__(self, obj, objtype=None):
        if obj is not None:
            # Then the method was called on an instance, not the class itself
            return partial(self, obj)
            # Some people might find it easier to phrase this 
            # partial(self.func, obj) in this case, which would be equivalent. I 
            # prefer doing partial(self, obj) since then I can place all the 
            # logic for calling in one place.
        else:
            # The method was called on the class, not a particular instance, 
            # so we're not going to do anything special. Functions return 
            # unbound methods (which typecheck their first arguments) in this 
            # case, which I've always thought was an iffy approach.
            return self

class Foo(object):
    @Test
    def bar(self):
        return "hello world"

f = Foo()
print f.bar()

就你得到的实际错误而言,我并不是100%肯定你为什么这么做。我不知道这不是Pylons古怪我不知道的。整个相关文件和完整的追溯可以帮助人们诊断问题。