我有一个容器,它包含一个对象的方法,可能还有一些描述符。我想通过检查是否有“ get ”方法来测试描述符是否已经被解包。令我惊讶的是,类方法的__get__
方法返回一个也具有__get__
方法的对象。你知道这个行为什么时候有用吗?它是否与派生类中的重写类方法有关?
import inspect
class K:
@classmethod
def cm(cls, a, b, c):
pass
def get_attr_info(attr):
try:
sig = inspect.signature(attr)
except:
sig = None
attr_info = [
('id ', id(attr),),
('type ', type(attr),),
('hasattr ', '__get__', hasattr(attr, '__get__'),),
('hasattr ', '__call__', hasattr(attr, '__call__'),),
('SIG: ', sig,)
]
return attr_info
get_label = lambda tpl: ' '.join([str(x) for x in tpl[0:-1]]).ljust(20)
kinst = K()
cm = object.__getattribute__(type(kinst), '__dict__')['cm']
try:
for idx in range(0, 5):
info = get_attr_info(cm)
print('\n' + '\n'.join([get_label(tpl) + str(tpl[-1]) for tpl in info]))
cm = cm.__get__(kinst, type(kinst))
except AttributeError:
print(idx)
输出为:
id 44545808
type <class 'classmethod'>
hasattr __get__ True
hasattr __call__ False
SIG: None
id 6437832
type <class 'method'>
hasattr __get__ True
hasattr __call__ True
SIG: (a, b, c)
id 6437832
type <class 'method'>
hasattr __get__ True
hasattr __call__ True
SIG: (a, b, c)
id 6437832
type <class 'method'>
hasattr __get__ True
hasattr __call__ True
SIG: (a, b, c)
id 6437832
type <class 'method'>
hasattr __get__ True
hasattr __call__ True
SIG: (a, b, c)
答案 0 :(得分:1)
由于未绑定的方法对象,您看到的特定行为过去有意义。回到Python 2,如果你做了
class Foo(object):
def useful_method(self):
do_stuff()
class Bar(object):
useful_method = Foo.useful_method
Foo.useful_method
将评估为未绑定的方法对象,类似于但与函数略有不同。未绑定的方法对象需要__get__
,因此Bar().useful_method
会自动绑定self
,就像Foo.useful_method
在定义{Bar
期间评估函数而不是未绑定方法一样1}}。
未绑定的方法对象和绑定的方法对象使用相同的类型实现,因此绑定的方法共享相同的__get__
方法未绑定的方法。但是对于绑定的方法对象,__get__
只返回绑定的方法对象,就好像没有涉及描述符逻辑一样。
现在,未绑定的方法已不存在,方法对象的__get__
方法是多余的,但尚未删除。
答案 1 :(得分:0)
关于为什么绑定方法是描述符的具体问题(为什么classmethod
对象应该很明显,对吧?),请参阅user's answer。
关于为什么需要允许非数据描述符的一般问题:编写它们会更痛苦。毕竟,编写非数据描述符最明显的方法通常是返回一个函数。并且函数必须是非数据描述符,否则方法将无法工作,从而无法将描述符添加到语言中。
例如,考虑the HOWTO中的“纯Python classmethod
”示例:
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
或者,更简单地说,考虑staticmethod
,即使在内置实现中,它也会在绑定时返回函数本身。
当然,您也可以通过使用自定义__call__
方法构建对象来实现其中任何一项,有时这值得做(例如,您可以使用classmethod
实现partial
,或者使用像partial
这样的东西,在这种情况下没有理由添加__get__
) - 但是当函数正常工作时,为什么不使用函数?
关于你是否可以找到这个功能的使用的更抽象的问题......嗯,当然。您可以做的事情包括:
__get__
中的参数,除了检查第二个是类型,或者第二个是None
,第一个是类型......)这些都不是你经常要做的事情,但它们也不是Python有任何理由阻止你做的事情。