"__get__"论点之一是否多余?



如上所述:

https://docs.python.org/3/reference/datamodel.html#object.__get__

传递给__get__方法的两个参数("self"除外(分别是访问属性的对象和类。第二个论点不是多余的吗?此外,当"类"也是对象时,为什么需要区分对象和类访问?

所以,在我看来,有两种可能性:

  • 属性从对象访问,在这种情况下,owner参数将等于类型(instance(,因此不会带来新信息
  • 属性从类("type"的对象(访问,在这种情况下,源对象仅位于owner参数中,实例None

在我看来,如果只使用一个参数(例如实例(,则可以实现相同的功能;类";是否。如果确实需要这些信息,可以使用isinstance(instance, type)进行检查。

那么,为什么需要两种论点呢?

它们分开的原因来自PEP 252 中的原始散文

__get__():一个可以用一个或两个参数调用的函数,用于从对象中检索属性值。这也被称为";绑定";操作,因为它可能返回"0";绑定方法";对象。第一个参数X是必须从中检索属性或将属性绑定到的对象。当X为None时,可选的第二个参数T应该是元对象,绑定操作可能返回一个限制为T实例的未绑定方法。当X和T都指定时,X应该是T的实例。绑定操作返回的确切内容取决于描述符的语义;例如,静态方法和类方法(见下文(会忽略实例,转而绑定到类型。

换句话说,这两个自变量允许区分";未绑定";描述符(对类调用的描述符(和";绑定";描述符(对实例调用的描述符(。classmethod(它使用owner参数而忽略instance参数(是您经常看到但并不真正考虑它的一个例子。

如果你总是使用";绑定";描述符,owner有点多余,因为instance应该是那种类型的实例。

也许更容易看到的是在纯python中实现的classmethod描述符:

class MyClassmethod(object):
def __init__(self, func):
self._func = func
def __get__(self, instance, owner = None):
# instance is ignored, `owner` is bound to the first arg
return self._func.__get__(owner)

class C:
@MyClassmethod
def func(cls, x):
print(cls)
print(x)
C.func(1)
C().func(2)
OUTPUT = '''
$ python3 t.py 
<class '__main__.C'>
1
<class '__main__.C'>
2
'''

或者考虑这个(有些不完整(cached_class_property:

class cached_class_property:
def __init__(self, fget):
self.fget = fget
def __get__(self, obj, owner):
val = self.fget(owner)
setattr(owner, self.fget.__name__, val)
return val

class C:
@cached_class_property
def f(self):
print('calculating...')
return 42

print(C.f)
print(C().f)
OUTPUT = '''
$ python3 t.py
calculating...
42
42
'''

注意,由于python3;未绑定";以及";绑定";方法不再是一个真正的概念,但api仍然存在于描述符级别——特别是类上的函数不再验证实例的类型是否与所有者匹配:

class C:
def d(self):
print(self)
class D:
pass
C().d()
C.d(D())
OUTPUT = '''
$ python3 t.py
<__main__.C object at 0x7f09576d3040>
<__main__.D object at 0x7f09576d3040>
$ python2 t.py
<__main__.C instance at 0x7efe2c8a7910>
Traceback (most recent call last):
File "t2.py", line 9, in <module>
C.d(D())
TypeError: unbound method d() must be called with C instance as first argument (got D instance instead)
'''

最新更新