我收到此错误
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'
答案 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
的实例为tc
,TestController
相当于tc.index()
或tc.index.__call__()
。
问题是,在Test.__call__(tc.index)
的来电中,我们丢失了对Test.__call__
的引用。定义tc
时不存在,因此无法保存它。此外,看起来Pylons在方法上执行了一些魔术,它期望Test.index
是用户定义的方法(具有tc.index
属性),而不是对象(它不是)。
我调用的方法在调用im_func
和Test.__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古怪我不知道的。整个相关文件和完整的追溯可以帮助人们诊断问题。