shx*_*hx2 6 python descriptor python-2.7 python-decorators
我很难理解当我尝试嵌套描述符/装饰器时会发生什么.我正在使用python 2.7.
例如,让我们的以下简化版本property和classmethod:
class MyProperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, obj, objtype=None):
print 'IN MyProperty.__get__'
return self.fget(obj)
class MyClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
print 'IN MyClassMethod.__get__'
def f(*args, **kwargs):
return self.f(objtype, *args, **kwargs)
return f
Run Code Online (Sandbox Code Playgroud)
试图嵌套它们:
class A(object):
# doesn't work:
@MyProperty
@MyClassMethod
def klsproperty(cls):
return 555
# works:
@MyProperty
def prop(self):
return 111
# works:
@MyClassMethod
def klsmethod(cls, x):
return x**2
% print A.klsproperty
IN MyProperty.__get__
...
TypeError: 'MyClassMethod' object is not callable
Run Code Online (Sandbox Code Playgroud)
该__get__内描述的方法MyClassMethod是没有得到调用.没有弄明白为什么,我试着投入(我认为)一个无操作描述符:
class NoopDescriptor(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
print 'IN NoopDescriptor.__get__'
return self.f.__get__(obj, objtype=objtype)
Run Code Online (Sandbox Code Playgroud)
尝试在嵌套中使用no-op描述符/装饰器:
class B(object):
# works:
@NoopDescriptor
@MyProperty
def prop1(self):
return 888
# doesn't work:
@MyProperty
@NoopDescriptor
def prop2(self):
return 999
% print B().prop1
IN NoopDescriptor.__get__
IN MyProperty.__get__
888
% print B().prop2
IN MyProperty.__get__
...
TypeError: 'NoopDescriptor' object is not callable
Run Code Online (Sandbox Code Playgroud)
我不明白为什么B().prop1有效而B().prop2不是.
问题:
object is not callable错误?MyClassProperty在重复使用MyClassMethod和MyProperty(classmethod和property)时定义的最佳方式是什么在这种情况下,当使用没有参数的装饰器时,使用它装饰的函数作为参数调用装饰器.使用装饰器的返回值代替装饰函数.所以:
@MyProperty
def prop(self):
...
Run Code Online (Sandbox Code Playgroud)
相当于:
def prop(self):
...
prop = MyProperty(prop)
Run Code Online (Sandbox Code Playgroud)
由于MyProperty实现了描述符协议,访问A.prop实际上会调用A.prop.__get__(),并且你已经定义__get__了调用被装饰的对象(在本例中是原始函数/方法),所以一切正常.
现在,在嵌套的情况下:
@MyProperty
@MyClassMethod
def prop(self):
...
Run Code Online (Sandbox Code Playgroud)
相当于:
def prop(self):
...
prop = MyClassMethod(prop) # prop is now instance of MyClassMethod
prop = MyProperty(prop) # prop is now instance of MyProperty
# (with fget == MyClassMethod instance)
Run Code Online (Sandbox Code Playgroud)
现在,和以前一样,访问A.prop实际上会调用A.prop.__get__()(in MyProperty)然后尝试调用MyClassMethod(被装饰并存储在fget属性中的对象)的实例.
但是MyClassMethod没有__call__定义方法,所以你得到了错误MyClassMethod is not callable.
并解决第二个问题:属性已经是一个类属性 - 在您的示例中,访问A.prop将返回类对象中A().prop属性的值,并将返回实例对象中的属性值(可以与如果实例没有覆盖它,则为类对象).
MyProperty如果将描述符协议应用于其包装对象,则可以使代码正常工作:
class MyProperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, obj, objtype=None):
print('IN MyProperty.__get__')
try:
return self.fget.__get__(obj, objtype)()
except AttributeError: # self.fget has no __get__ method
return self.fget(obj)
Run Code Online (Sandbox Code Playgroud)
现在您的示例代码可以运行:
class A(object):
@MyProperty
@MyClassMethod
def klsproperty(cls):
return 555
print(A.klsproperty)
Run Code Online (Sandbox Code Playgroud)
输出是:
IN MyProperty.__get__
IN MyClassMethod.__get__
555
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1126 次 |
| 最近记录: |